From 141dfde72e874079dbb4814a5214066024890bf4 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 1 Sep 2026 06:47:23 -0500 Subject: [PATCH 01/69] Point vendor.sh at libbson 2.5.0 --- vendor.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vendor.sh b/vendor.sh index af34580..5e44c20 100644 --- a/vendor.sh +++ b/vendor.sh @@ -1,6 +1,6 @@ #!/bin/bash set -eu -VERSION="1.27.2" +VERSION="2.5.0" rm -rf mongo-c-driver git clone git@github.com:mongodb/mongo-c-driver.git pushd mongo-c-driver @@ -21,7 +21,7 @@ rsync -r mongo-c-driver/cmake-build/src/common/*.[hc] bsonjs/common/ rsync -r mongo-c-driver/cmake-build/src/libbson/src/bson/*.[hc] bsonjs/bson/ -# Ignore autogenerated bson-config.h -git diff -- bsonjs/bson/bson-config.h | tee -echo "**** Review libbson's autogenerated src/bson/bson-config.h (above) for newly added (or removed) macros ****" -git checkout -- bsonjs/bson/bson-config.h +# Ignore autogenerated config.h +git diff -- bsonjs/bson/config.h | tee +echo "**** Review libbson's autogenerated src/bson/config.h (above) for newly added (or removed) macros ****" +git checkout -- bsonjs/bson/config.h From b7719f7e482efeea0c993e14c7126bd1813c5c3f Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 1 Sep 2026 06:52:41 -0500 Subject: [PATCH 02/69] Vendor libbson 2.5.0 sources --- bsonjs/bson/bcon.h | 245 -- bsonjs/bson/bson-atomic.c | 269 -- bsonjs/bson/bson-atomic.h | 611 --- bsonjs/bson/{bcon.c => bson-bcon.c} | 486 +-- bsonjs/bson/bson-bcon.h | 249 ++ bsonjs/bson/bson-clock.c | 46 +- bsonjs/bson/bson-clock.h | 14 +- bsonjs/bson/bson-cmp.h | 173 - bsonjs/bson/bson-context-private.h | 25 +- bsonjs/bson/bson-context.c | 210 +- bsonjs/bson/bson-context.h | 16 +- bsonjs/bson/bson-decimal128.c | 224 +- bsonjs/bson/bson-decimal128.h | 22 +- bsonjs/bson/bson-endian.h | 147 +- .../{bson-error.h => bson-error-private.h} | 35 +- bsonjs/bson/bson-iso8601-private.h | 21 +- bsonjs/bson/bson-iso8601.c | 139 +- bsonjs/bson/bson-iter.c | 1048 ++--- bsonjs/bson/bson-iter.h | 379 +- bsonjs/bson/bson-json-private.h | 3 +- bsonjs/bson/bson-json.c | 1364 +++---- bsonjs/bson/bson-json.h | 48 +- bsonjs/bson/bson-keys.c | 25 +- bsonjs/bson/bson-keys.h | 8 +- bsonjs/bson/bson-md5.c | 24 - bsonjs/bson/bson-md5.h | 87 - bsonjs/bson/bson-memory.h | 73 - bsonjs/bson/bson-oid.c | 173 +- bsonjs/bson/bson-oid.h | 83 +- bsonjs/bson/bson-prelude.h | 2 +- bsonjs/bson/bson-private.h | 106 - bsonjs/bson/bson-reader.c | 210 +- bsonjs/bson/bson-reader.h | 56 +- bsonjs/bson/bson-string.c | 433 +- bsonjs/bson/bson-string.h | 87 +- bsonjs/bson/bson-timegm-private.h | 8 +- bsonjs/bson/bson-timegm.c | 318 +- bsonjs/bson/bson-types.h | 252 +- bsonjs/bson/bson-utf8.c | 223 +- bsonjs/bson/bson-utf8.h | 24 +- bsonjs/bson/bson-value.c | 109 +- bsonjs/bson/bson-value.h | 12 +- bsonjs/bson/bson-vector-private.h | 64 + bsonjs/bson/bson-vector.c | 686 ++++ bsonjs/bson/bson-vector.h | 615 +++ bsonjs/bson/bson-version-functions.c | 18 +- bsonjs/bson/bson-version-functions.h | 22 +- bsonjs/bson/bson-writer.c | 67 +- bsonjs/bson/bson-writer.h | 36 +- bsonjs/bson/bson.c | 3495 +++++++---------- bsonjs/bson/bson.h | 688 ++-- bsonjs/bson/bson_t-private.h | 106 + bsonjs/bson/bson_t.h | 61 + bsonjs/bson/{bson-compat.h => compat.h} | 95 +- bsonjs/bson/{bson-config.h => config.h} | 31 +- bsonjs/bson/{bson-error.c => error.c} | 67 +- bsonjs/bson/error.h | 94 + bsonjs/bson/{bson-macros.h => macros.h} | 224 +- bsonjs/bson/{bson-memory.c => memory.c} | 216 +- bsonjs/bson/memory.h | 71 + bsonjs/bson/validate-private.h | 37 + bsonjs/bson/validate.c | 568 +++ bsonjs/bson/{bson-version.h => version.h} | 17 +- bsonjs/common/common-atomic-private.h | 657 ++++ bsonjs/common/common-atomic.c | 251 ++ bsonjs/common/common-b64-private.h | 26 +- bsonjs/common/common-b64.c | 146 +- bsonjs/common/common-bits-private.h | 50 + .../{bson-dsl.h => common-bson-dsl-private.h} | 1220 +++--- bsonjs/common/common-json-private.h | 397 ++ bsonjs/common/common-json.c | 765 ++++ bsonjs/common/common-macros-private.h | 106 +- bsonjs/common/common-md5-private.h | 30 +- bsonjs/common/common-md5.c | 198 +- bsonjs/common/common-oid-private.h | 36 + bsonjs/common/common-oid.c | 33 + bsonjs/common/common-prelude.h | 8 +- bsonjs/common/common-string-private.h | 673 ++++ bsonjs/common/common-string.c | 372 ++ bsonjs/common/common-thread-private.h | 162 +- bsonjs/common/common-thread.c | 38 +- bsonjs/common/common-utf8-private.h | 182 + bsonjs/common/mlib/ckdint.h | 679 ++++ bsonjs/common/mlib/cmp.h | 132 + bsonjs/common/mlib/config.h | 419 ++ bsonjs/common/mlib/duration.h | 399 ++ bsonjs/common/mlib/intencode.h | 350 ++ bsonjs/common/mlib/intutil.h | 145 + bsonjs/common/mlib/loop.h | 165 + bsonjs/common/mlib/platform.h | 53 + bsonjs/common/mlib/str.h | 1239 ++++++ bsonjs/common/mlib/str_vec.h | 31 + bsonjs/common/mlib/test.h | 334 ++ bsonjs/common/mlib/time_point.h | 346 ++ bsonjs/common/mlib/timer.h | 173 + bsonjs/common/mlib/vec.th | 470 +++ bsonjs/jsonsl/jsonsl.c | 221 +- bsonjs/jsonsl/jsonsl.h | 60 +- 98 files changed, 17097 insertions(+), 8834 deletions(-) delete mode 100644 bsonjs/bson/bcon.h delete mode 100644 bsonjs/bson/bson-atomic.c delete mode 100644 bsonjs/bson/bson-atomic.h rename bsonjs/bson/{bcon.c => bson-bcon.c} (56%) create mode 100644 bsonjs/bson/bson-bcon.h delete mode 100644 bsonjs/bson/bson-cmp.h rename bsonjs/bson/{bson-error.h => bson-error-private.h} (53%) delete mode 100644 bsonjs/bson/bson-md5.c delete mode 100644 bsonjs/bson/bson-md5.h delete mode 100644 bsonjs/bson/bson-memory.h delete mode 100644 bsonjs/bson/bson-private.h create mode 100644 bsonjs/bson/bson-vector-private.h create mode 100644 bsonjs/bson/bson-vector.c create mode 100644 bsonjs/bson/bson-vector.h create mode 100644 bsonjs/bson/bson_t-private.h create mode 100644 bsonjs/bson/bson_t.h rename bsonjs/bson/{bson-compat.h => compat.h} (68%) rename bsonjs/bson/{bson-config.h => config.h} (90%) rename bsonjs/bson/{bson-error.c => error.c} (77%) create mode 100644 bsonjs/bson/error.h rename bsonjs/bson/{bson-macros.h => macros.h} (50%) rename bsonjs/bson/{bson-memory.c => memory.c} (63%) create mode 100644 bsonjs/bson/memory.h create mode 100644 bsonjs/bson/validate-private.h create mode 100644 bsonjs/bson/validate.c rename bsonjs/bson/{bson-version.h => version.h} (87%) create mode 100644 bsonjs/common/common-atomic-private.h create mode 100644 bsonjs/common/common-atomic.c create mode 100644 bsonjs/common/common-bits-private.h rename bsonjs/common/{bson-dsl.h => common-bson-dsl-private.h} (57%) create mode 100644 bsonjs/common/common-json-private.h create mode 100644 bsonjs/common/common-json.c create mode 100644 bsonjs/common/common-oid-private.h create mode 100644 bsonjs/common/common-oid.c create mode 100644 bsonjs/common/common-string-private.h create mode 100644 bsonjs/common/common-string.c create mode 100644 bsonjs/common/common-utf8-private.h create mode 100644 bsonjs/common/mlib/ckdint.h create mode 100644 bsonjs/common/mlib/cmp.h create mode 100644 bsonjs/common/mlib/config.h create mode 100644 bsonjs/common/mlib/duration.h create mode 100644 bsonjs/common/mlib/intencode.h create mode 100644 bsonjs/common/mlib/intutil.h create mode 100644 bsonjs/common/mlib/loop.h create mode 100644 bsonjs/common/mlib/platform.h create mode 100644 bsonjs/common/mlib/str.h create mode 100644 bsonjs/common/mlib/str_vec.h create mode 100644 bsonjs/common/mlib/test.h create mode 100644 bsonjs/common/mlib/time_point.h create mode 100644 bsonjs/common/mlib/timer.h create mode 100644 bsonjs/common/mlib/vec.th diff --git a/bsonjs/bson/bcon.h b/bsonjs/bson/bcon.h deleted file mode 100644 index d35365e..0000000 --- a/bsonjs/bson/bcon.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - * @file bcon.h - * @brief BCON (BSON C Object Notation) Declarations - */ - -#include - -/* Copyright 2009-2013 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. - */ - -#ifndef BCON_H_ -#define BCON_H_ - -#include "bson.h" - - -BSON_BEGIN_DECLS - - -#define BCON_STACK_MAX 100 - -#define BCON_ENSURE_DECLARE(fun, type) \ - static BSON_INLINE type bcon_ensure_##fun (type _t) \ - { \ - return _t; \ - } - -#define BCON_ENSURE(fun, val) bcon_ensure_##fun (val) - -#define BCON_ENSURE_STORAGE(fun, val) bcon_ensure_##fun (&(val)) - -BCON_ENSURE_DECLARE (const_char_ptr, const char *) -BCON_ENSURE_DECLARE (const_char_ptr_ptr, const char **) -BCON_ENSURE_DECLARE (double, double) -BCON_ENSURE_DECLARE (double_ptr, double *) -BCON_ENSURE_DECLARE (const_bson_ptr, const bson_t *) -BCON_ENSURE_DECLARE (bson_ptr, bson_t *) -BCON_ENSURE_DECLARE (subtype, bson_subtype_t) -BCON_ENSURE_DECLARE (subtype_ptr, bson_subtype_t *) -BCON_ENSURE_DECLARE (const_uint8_ptr, const uint8_t *) -BCON_ENSURE_DECLARE (const_uint8_ptr_ptr, const uint8_t **) -BCON_ENSURE_DECLARE (uint32, uint32_t) -BCON_ENSURE_DECLARE (uint32_ptr, uint32_t *) -BCON_ENSURE_DECLARE (const_oid_ptr, const bson_oid_t *) -BCON_ENSURE_DECLARE (const_oid_ptr_ptr, const bson_oid_t **) -BCON_ENSURE_DECLARE (int32, int32_t) -BCON_ENSURE_DECLARE (int32_ptr, int32_t *) -BCON_ENSURE_DECLARE (int64, int64_t) -BCON_ENSURE_DECLARE (int64_ptr, int64_t *) -BCON_ENSURE_DECLARE (const_decimal128_ptr, const bson_decimal128_t *) -BCON_ENSURE_DECLARE (bool, bool) -BCON_ENSURE_DECLARE (bool_ptr, bool *) -BCON_ENSURE_DECLARE (bson_type, bson_type_t) -BCON_ENSURE_DECLARE (bson_iter_ptr, bson_iter_t *) -BCON_ENSURE_DECLARE (const_bson_iter_ptr, const bson_iter_t *) - -#define BCON_UTF8(_val) BCON_MAGIC, BCON_TYPE_UTF8, BCON_ENSURE (const_char_ptr, (_val)) -#define BCON_DOUBLE(_val) BCON_MAGIC, BCON_TYPE_DOUBLE, BCON_ENSURE (double, (_val)) -#define BCON_DOCUMENT(_val) BCON_MAGIC, BCON_TYPE_DOCUMENT, BCON_ENSURE (const_bson_ptr, (_val)) -#define BCON_ARRAY(_val) BCON_MAGIC, BCON_TYPE_ARRAY, BCON_ENSURE (const_bson_ptr, (_val)) -#define BCON_BIN(_subtype, _binary, _length) \ - BCON_MAGIC, BCON_TYPE_BIN, BCON_ENSURE (subtype, (_subtype)), BCON_ENSURE (const_uint8_ptr, (_binary)), \ - BCON_ENSURE (uint32, (_length)) -#define BCON_UNDEFINED BCON_MAGIC, BCON_TYPE_UNDEFINED -#define BCON_OID(_val) BCON_MAGIC, BCON_TYPE_OID, BCON_ENSURE (const_oid_ptr, (_val)) -#define BCON_BOOL(_val) BCON_MAGIC, BCON_TYPE_BOOL, BCON_ENSURE (bool, (_val)) -#define BCON_DATE_TIME(_val) BCON_MAGIC, BCON_TYPE_DATE_TIME, BCON_ENSURE (int64, (_val)) -#define BCON_NULL BCON_MAGIC, BCON_TYPE_NULL -#define BCON_REGEX(_regex, _flags) \ - BCON_MAGIC, BCON_TYPE_REGEX, BCON_ENSURE (const_char_ptr, (_regex)), BCON_ENSURE (const_char_ptr, (_flags)) -#define BCON_DBPOINTER(_collection, _oid) \ - BCON_MAGIC, BCON_TYPE_DBPOINTER, BCON_ENSURE (const_char_ptr, (_collection)), BCON_ENSURE (const_oid_ptr, (_oid)) -#define BCON_CODE(_val) BCON_MAGIC, BCON_TYPE_CODE, BCON_ENSURE (const_char_ptr, (_val)) -#define BCON_SYMBOL(_val) BCON_MAGIC, BCON_TYPE_SYMBOL, BCON_ENSURE (const_char_ptr, (_val)) -#define BCON_CODEWSCOPE(_js, _scope) \ - BCON_MAGIC, BCON_TYPE_CODEWSCOPE, BCON_ENSURE (const_char_ptr, (_js)), BCON_ENSURE (const_bson_ptr, (_scope)) -#define BCON_INT32(_val) BCON_MAGIC, BCON_TYPE_INT32, BCON_ENSURE (int32, (_val)) -#define BCON_TIMESTAMP(_timestamp, _increment) \ - BCON_MAGIC, BCON_TYPE_TIMESTAMP, BCON_ENSURE (int32, (_timestamp)), BCON_ENSURE (int32, (_increment)) -#define BCON_INT64(_val) BCON_MAGIC, BCON_TYPE_INT64, BCON_ENSURE (int64, (_val)) -#define BCON_DECIMAL128(_val) BCON_MAGIC, BCON_TYPE_DECIMAL128, BCON_ENSURE (const_decimal128_ptr, (_val)) -#define BCON_MAXKEY BCON_MAGIC, BCON_TYPE_MAXKEY -#define BCON_MINKEY BCON_MAGIC, BCON_TYPE_MINKEY -#define BCON(_val) BCON_MAGIC, BCON_TYPE_BCON, BCON_ENSURE (const_bson_ptr, (_val)) -#define BCON_ITER(_val) BCON_MAGIC, BCON_TYPE_ITER, BCON_ENSURE (const_bson_iter_ptr, (_val)) - -#define BCONE_UTF8(_val) BCONE_MAGIC, BCON_TYPE_UTF8, BCON_ENSURE_STORAGE (const_char_ptr_ptr, (_val)) -#define BCONE_DOUBLE(_val) BCONE_MAGIC, BCON_TYPE_DOUBLE, BCON_ENSURE_STORAGE (double_ptr, (_val)) -#define BCONE_DOCUMENT(_val) BCONE_MAGIC, BCON_TYPE_DOCUMENT, BCON_ENSURE_STORAGE (bson_ptr, (_val)) -#define BCONE_ARRAY(_val) BCONE_MAGIC, BCON_TYPE_ARRAY, BCON_ENSURE_STORAGE (bson_ptr, (_val)) -#define BCONE_BIN(subtype, binary, length) \ - BCONE_MAGIC, BCON_TYPE_BIN, BCON_ENSURE_STORAGE (subtype_ptr, (subtype)), \ - BCON_ENSURE_STORAGE (const_uint8_ptr_ptr, (binary)), BCON_ENSURE_STORAGE (uint32_ptr, (length)) -#define BCONE_UNDEFINED BCONE_MAGIC, BCON_TYPE_UNDEFINED -#define BCONE_OID(_val) BCONE_MAGIC, BCON_TYPE_OID, BCON_ENSURE_STORAGE (const_oid_ptr_ptr, (_val)) -#define BCONE_BOOL(_val) BCONE_MAGIC, BCON_TYPE_BOOL, BCON_ENSURE_STORAGE (bool_ptr, (_val)) -#define BCONE_DATE_TIME(_val) BCONE_MAGIC, BCON_TYPE_DATE_TIME, BCON_ENSURE_STORAGE (int64_ptr, (_val)) -#define BCONE_NULL BCONE_MAGIC, BCON_TYPE_NULL -#define BCONE_REGEX(_regex, _flags) \ - BCONE_MAGIC, BCON_TYPE_REGEX, BCON_ENSURE_STORAGE (const_char_ptr_ptr, (_regex)), \ - BCON_ENSURE_STORAGE (const_char_ptr_ptr, (_flags)) -#define BCONE_DBPOINTER(_collection, _oid) \ - BCONE_MAGIC, BCON_TYPE_DBPOINTER, BCON_ENSURE_STORAGE (const_char_ptr_ptr, (_collection)), \ - BCON_ENSURE_STORAGE (const_oid_ptr_ptr, (_oid)) -#define BCONE_CODE(_val) BCONE_MAGIC, BCON_TYPE_CODE, BCON_ENSURE_STORAGE (const_char_ptr_ptr, (_val)) -#define BCONE_SYMBOL(_val) BCONE_MAGIC, BCON_TYPE_SYMBOL, BCON_ENSURE_STORAGE (const_char_ptr_ptr, (_val)) -#define BCONE_CODEWSCOPE(_js, _scope) \ - BCONE_MAGIC, BCON_TYPE_CODEWSCOPE, BCON_ENSURE_STORAGE (const_char_ptr_ptr, (_js)), \ - BCON_ENSURE_STORAGE (bson_ptr, (_scope)) -#define BCONE_INT32(_val) BCONE_MAGIC, BCON_TYPE_INT32, BCON_ENSURE_STORAGE (int32_ptr, (_val)) -#define BCONE_TIMESTAMP(_timestamp, _increment) \ - BCONE_MAGIC, BCON_TYPE_TIMESTAMP, BCON_ENSURE_STORAGE (int32_ptr, (_timestamp)), \ - BCON_ENSURE_STORAGE (int32_ptr, (_increment)) -#define BCONE_INT64(_val) BCONE_MAGIC, BCON_TYPE_INT64, BCON_ENSURE_STORAGE (int64_ptr, (_val)) -#define BCONE_DECIMAL128(_val) BCONE_MAGIC, BCON_TYPE_DECIMAL128, BCON_ENSURE_STORAGE (const_decimal128_ptr, (_val)) -#define BCONE_MAXKEY BCONE_MAGIC, BCON_TYPE_MAXKEY -#define BCONE_MINKEY BCONE_MAGIC, BCON_TYPE_MINKEY -#define BCONE_SKIP(_val) BCONE_MAGIC, BCON_TYPE_SKIP, BCON_ENSURE (bson_type, (_val)) -#define BCONE_ITER(_val) BCONE_MAGIC, BCON_TYPE_ITER, BCON_ENSURE_STORAGE (bson_iter_ptr, (_val)) - -#define BCON_MAGIC bson_bcon_magic () -#define BCONE_MAGIC bson_bcone_magic () - -typedef enum { - BCON_TYPE_UTF8, - BCON_TYPE_DOUBLE, - BCON_TYPE_DOCUMENT, - BCON_TYPE_ARRAY, - BCON_TYPE_BIN, - BCON_TYPE_UNDEFINED, - BCON_TYPE_OID, - BCON_TYPE_BOOL, - BCON_TYPE_DATE_TIME, - BCON_TYPE_NULL, - BCON_TYPE_REGEX, - BCON_TYPE_DBPOINTER, - BCON_TYPE_CODE, - BCON_TYPE_SYMBOL, - BCON_TYPE_CODEWSCOPE, - BCON_TYPE_INT32, - BCON_TYPE_TIMESTAMP, - BCON_TYPE_INT64, - BCON_TYPE_DECIMAL128, - BCON_TYPE_MAXKEY, - BCON_TYPE_MINKEY, - BCON_TYPE_BCON, - BCON_TYPE_ARRAY_START, - BCON_TYPE_ARRAY_END, - BCON_TYPE_DOC_START, - BCON_TYPE_DOC_END, - BCON_TYPE_END, - BCON_TYPE_RAW, - BCON_TYPE_SKIP, - BCON_TYPE_ITER, - BCON_TYPE_ERROR, -} bcon_type_t; - -typedef struct bcon_append_ctx_frame { - int i; - bool is_array; - bson_t bson; -} bcon_append_ctx_frame_t; - -typedef struct bcon_extract_ctx_frame { - int i; - bool is_array; - bson_iter_t iter; -} bcon_extract_ctx_frame_t; - -typedef struct _bcon_append_ctx_t { - bcon_append_ctx_frame_t stack[BCON_STACK_MAX]; - int n; -} bcon_append_ctx_t; - -typedef struct _bcon_extract_ctx_t { - bcon_extract_ctx_frame_t stack[BCON_STACK_MAX]; - int n; -} bcon_extract_ctx_t; - -BSON_EXPORT (void) -bcon_append (bson_t *bson, ...) BSON_GNUC_NULL_TERMINATED; -BSON_EXPORT (void) -bcon_append_ctx (bson_t *bson, bcon_append_ctx_t *ctx, ...) BSON_GNUC_NULL_TERMINATED; -BSON_EXPORT (void) -bcon_append_ctx_va (bson_t *bson, bcon_append_ctx_t *ctx, va_list *va); -BSON_EXPORT (void) -bcon_append_ctx_init (bcon_append_ctx_t *ctx); - -BSON_EXPORT (void) -bcon_extract_ctx_init (bcon_extract_ctx_t *ctx); - -BSON_EXPORT (void) -bcon_extract_ctx (bson_t *bson, bcon_extract_ctx_t *ctx, ...) BSON_GNUC_NULL_TERMINATED; - -BSON_EXPORT (bool) -bcon_extract_ctx_va (bson_t *bson, bcon_extract_ctx_t *ctx, va_list *ap); - -BSON_EXPORT (bool) -bcon_extract (bson_t *bson, ...) BSON_GNUC_NULL_TERMINATED; - -BSON_EXPORT (bool) -bcon_extract_va (bson_t *bson, bcon_extract_ctx_t *ctx, ...) BSON_GNUC_NULL_TERMINATED; - -BSON_EXPORT (bson_t *) -bcon_new (void *unused, ...) BSON_GNUC_NULL_TERMINATED; - -/** - * The bcon_..() functions are all declared with __attribute__((sentinel)). - * - * From GCC manual for "sentinel": "A valid NULL in this context is defined as - * zero with any pointer type. If your system defines the NULL macro with an - * integer type then you need to add an explicit cast." - * Case in point: GCC on Solaris (at least) - */ -#define BCON_APPEND(_bson, ...) bcon_append ((_bson), __VA_ARGS__, (void *) NULL) -#define BCON_APPEND_CTX(_bson, _ctx, ...) bcon_append_ctx ((_bson), (_ctx), __VA_ARGS__, (void *) NULL) - -#define BCON_EXTRACT(_bson, ...) bcon_extract ((_bson), __VA_ARGS__, (void *) NULL) - -#define BCON_EXTRACT_CTX(_bson, _ctx, ...) bcon_extract ((_bson), (_ctx), __VA_ARGS__, (void *) NULL) - -#define BCON_NEW(...) bcon_new (NULL, __VA_ARGS__, (void *) NULL) - -BSON_EXPORT (const char *) -bson_bcon_magic (void) BSON_GNUC_PURE; -BSON_EXPORT (const char *) -bson_bcone_magic (void) BSON_GNUC_PURE; - - -BSON_END_DECLS - - -#endif diff --git a/bsonjs/bson/bson-atomic.c b/bsonjs/bson/bson-atomic.c deleted file mode 100644 index 18dd4ed..0000000 --- a/bsonjs/bson/bson-atomic.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright 2014 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 - -#ifdef BSON_OS_UNIX -/* For sched_yield() */ -#include -#endif - -int32_t -bson_atomic_int_add (volatile int32_t *p, int32_t n) -{ - return n + bson_atomic_int32_fetch_add ((DECL_ATOMIC_INTEGRAL_INT32 *) p, n, bson_memory_order_seq_cst); -} - -int64_t -bson_atomic_int64_add (volatile int64_t *p, int64_t n) -{ - return n + bson_atomic_int64_fetch_add (p, n, bson_memory_order_seq_cst); -} - -void -bson_thrd_yield (void) -{ - BSON_IF_WINDOWS (SwitchToThread ();) - BSON_IF_POSIX (sched_yield ();) -} - -void -bson_memory_barrier (void) -{ - bson_atomic_thread_fence (); -} - -/** - * Some platforms do not support compiler intrinsics for atomic operations. - * We emulate that here using a spin lock and regular arithmetic operations - */ -static int8_t gEmulAtomicLock = 0; - -static void -_lock_emul_atomic (void) -{ - int i; - if (bson_atomic_int8_compare_exchange_weak (&gEmulAtomicLock, 0, 1, bson_memory_order_acquire) == 0) { - /* Successfully took the spinlock */ - return; - } - /* Failed. Try taking ten more times, then begin sleeping. */ - for (i = 0; i < 10; ++i) { - if (bson_atomic_int8_compare_exchange_weak (&gEmulAtomicLock, 0, 1, bson_memory_order_acquire) == 0) { - /* Succeeded in taking the lock */ - return; - } - } - /* Still don't have the lock. Spin and yield */ - while (bson_atomic_int8_compare_exchange_weak (&gEmulAtomicLock, 0, 1, bson_memory_order_acquire) != 0) { - bson_thrd_yield (); - } -} - -static void -_unlock_emul_atomic (void) -{ - int64_t rv = bson_atomic_int8_exchange (&gEmulAtomicLock, 0, bson_memory_order_release); - BSON_ASSERT (rv == 1 && "Released atomic lock while not holding it"); -} - -int64_t -_bson_emul_atomic_int64_fetch_add (volatile int64_t *p, int64_t n, enum bson_memory_order _unused) -{ - int64_t ret; - - BSON_UNUSED (_unused); - - _lock_emul_atomic (); - ret = *p; - *p += n; - _unlock_emul_atomic (); - return ret; -} - -int64_t -_bson_emul_atomic_int64_exchange (volatile int64_t *p, int64_t n, enum bson_memory_order _unused) -{ - int64_t ret; - - BSON_UNUSED (_unused); - - _lock_emul_atomic (); - ret = *p; - *p = n; - _unlock_emul_atomic (); - return ret; -} - -int64_t -_bson_emul_atomic_int64_compare_exchange_strong (volatile int64_t *p, - int64_t expect_value, - int64_t new_value, - enum bson_memory_order _unused) -{ - int64_t ret; - - BSON_UNUSED (_unused); - - _lock_emul_atomic (); - ret = *p; - if (ret == expect_value) { - *p = new_value; - } - _unlock_emul_atomic (); - return ret; -} - -int64_t -_bson_emul_atomic_int64_compare_exchange_weak (volatile int64_t *p, - int64_t expect_value, - int64_t new_value, - enum bson_memory_order order) -{ - /* We're emulating. We can't do a weak version. */ - return _bson_emul_atomic_int64_compare_exchange_strong (p, expect_value, new_value, order); -} - - -int32_t -_bson_emul_atomic_int32_fetch_add (volatile int32_t *p, int32_t n, enum bson_memory_order _unused) -{ - int32_t ret; - - BSON_UNUSED (_unused); - - _lock_emul_atomic (); - ret = *p; - *p += n; - _unlock_emul_atomic (); - return ret; -} - -int32_t -_bson_emul_atomic_int32_exchange (volatile int32_t *p, int32_t n, enum bson_memory_order _unused) -{ - int32_t ret; - - BSON_UNUSED (_unused); - - _lock_emul_atomic (); - ret = *p; - *p = n; - _unlock_emul_atomic (); - return ret; -} - -int32_t -_bson_emul_atomic_int32_compare_exchange_strong (volatile int32_t *p, - int32_t expect_value, - int32_t new_value, - enum bson_memory_order _unused) -{ - int32_t ret; - - BSON_UNUSED (_unused); - - _lock_emul_atomic (); - ret = *p; - if (ret == expect_value) { - *p = new_value; - } - _unlock_emul_atomic (); - return ret; -} - -int32_t -_bson_emul_atomic_int32_compare_exchange_weak (volatile int32_t *p, - int32_t expect_value, - int32_t new_value, - enum bson_memory_order order) -{ - /* We're emulating. We can't do a weak version. */ - return _bson_emul_atomic_int32_compare_exchange_strong (p, expect_value, new_value, order); -} - - -int -_bson_emul_atomic_int_fetch_add (volatile int *p, int n, enum bson_memory_order _unused) -{ - int ret; - - BSON_UNUSED (_unused); - - _lock_emul_atomic (); - ret = *p; - *p += n; - _unlock_emul_atomic (); - return ret; -} - -int -_bson_emul_atomic_int_exchange (volatile int *p, int n, enum bson_memory_order _unused) -{ - int ret; - - BSON_UNUSED (_unused); - - _lock_emul_atomic (); - ret = *p; - *p = n; - _unlock_emul_atomic (); - return ret; -} - -int -_bson_emul_atomic_int_compare_exchange_strong (volatile int *p, - 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) { - *p = new_value; - } - _unlock_emul_atomic (); - return ret; -} - -int -_bson_emul_atomic_int_compare_exchange_weak (volatile int *p, - 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/bsonjs/bson/bson-atomic.h b/bsonjs/bson/bson-atomic.h deleted file mode 100644 index 60ab74c..0000000 --- a/bsonjs/bson/bson-atomic.h +++ /dev/null @@ -1,611 +0,0 @@ -/* - * Copyright 2013-2014 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 - - -#ifndef BSON_ATOMIC_H -#define BSON_ATOMIC_H - - -#include -#include -#include - -#ifdef _MSC_VER -#include -#endif - - -BSON_BEGIN_DECLS - -enum bson_memory_order { - bson_memory_order_seq_cst, - bson_memory_order_acquire, - bson_memory_order_release, - bson_memory_order_relaxed, - bson_memory_order_acq_rel, - bson_memory_order_consume, -}; - -#if defined(_M_ARM) /* MSVC memorder atomics are only avail on ARM */ -#define MSVC_MEMORDER_SUFFIX(X) X -#else -#define MSVC_MEMORDER_SUFFIX(X) -#endif - -#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 -#endif - -/* Not all GCC-like compilers support the current __atomic built-ins. Older - * GCC (pre-5) used different built-ins named with the __sync prefix. When - * compiling with such older GCC versions, it is necessary to use the applicable - * functions, which requires redefining BSON_IF_GNU_LIKE and defining the - * additional BSON_IF_GNU_LEGACY_ATOMICS macro here. */ -#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(...) -#endif - -/* CDRIVER-4229 zSeries with gcc 4.8.4 produces illegal instructions for int and - * int32 atomic intrinsics. */ -#if defined(__s390__) || defined(__s390x__) || defined(__zarch__) -#define BSON_EMULATE_INT32 -#define BSON_EMULATE_INT -#endif - -/* 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: \ - BSON_IF_MSVC (return MSVC_Intrinsic (__VA_ARGS__);) \ - BSON_IF_GNU_LIKE (return GNU_Intrinsic (__VA_ARGS__, __ATOMIC_ACQ_REL);) \ - BSON_IF_GNU_LEGACY_ATOMICS (return GNU_Legacy_Intrinsic (__VA_ARGS__);) \ - case bson_memory_order_seq_cst: \ - BSON_IF_MSVC (return MSVC_Intrinsic (__VA_ARGS__);) \ - BSON_IF_GNU_LIKE (return GNU_Intrinsic (__VA_ARGS__, __ATOMIC_SEQ_CST);) \ - BSON_IF_GNU_LEGACY_ATOMICS (return GNU_Legacy_Intrinsic (__VA_ARGS__);) \ - case bson_memory_order_acquire: \ - BSON_IF_MSVC (return BSON_CONCAT (MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX (_acq)) (__VA_ARGS__);) \ - BSON_IF_GNU_LIKE (return GNU_Intrinsic (__VA_ARGS__, __ATOMIC_ACQUIRE);) \ - BSON_IF_GNU_LEGACY_ATOMICS (return GNU_Legacy_Intrinsic (__VA_ARGS__);) \ - case bson_memory_order_consume: \ - BSON_IF_MSVC (return BSON_CONCAT (MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX (_acq)) (__VA_ARGS__);) \ - BSON_IF_GNU_LIKE (return GNU_Intrinsic (__VA_ARGS__, __ATOMIC_CONSUME);) \ - BSON_IF_GNU_LEGACY_ATOMICS (return GNU_Legacy_Intrinsic (__VA_ARGS__);) \ - case bson_memory_order_release: \ - BSON_IF_MSVC (return BSON_CONCAT (MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX (_rel)) (__VA_ARGS__);) \ - BSON_IF_GNU_LIKE (return GNU_Intrinsic (__VA_ARGS__, __ATOMIC_RELEASE);) \ - BSON_IF_GNU_LEGACY_ATOMICS (return GNU_Legacy_Intrinsic (__VA_ARGS__);) \ - case bson_memory_order_relaxed: \ - BSON_IF_MSVC (return BSON_CONCAT (MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX (_nf)) (__VA_ARGS__);) \ - BSON_IF_GNU_LIKE (return GNU_Intrinsic (__VA_ARGS__, __ATOMIC_RELAXED);) \ - BSON_IF_GNU_LEGACY_ATOMICS (return GNU_Legacy_Intrinsic (__VA_ARGS__);) \ - default: \ - BSON_UNREACHABLE ("Invalid bson_memory_order value"); \ - } \ - } 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;) \ - } 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;) \ - } while (0) - - -#define DECL_ATOMIC_INTEGRAL(NamePart, Type, VCIntrinSuffix) \ - static BSON_INLINE Type bson_atomic_##NamePart##_fetch_add ( \ - Type volatile *a, Type addend, enum bson_memory_order ord) \ - { \ - DEF_ATOMIC_OP (BSON_CONCAT (_InterlockedExchangeAdd, VCIntrinSuffix), \ - __atomic_fetch_add, \ - __sync_fetch_and_add, \ - ord, \ - a, \ - addend); \ - } \ - \ - static BSON_INLINE Type bson_atomic_##NamePart##_fetch_sub ( \ - Type volatile *a, Type subtrahend, enum bson_memory_order ord) \ - { \ - /* MSVC doesn't have a subtract intrinsic, so just reuse addition */ \ - BSON_IF_MSVC (return bson_atomic_##NamePart##_fetch_add (a, -subtrahend, ord);) \ - BSON_IF_GNU_LIKE (DEF_ATOMIC_OP (~, __atomic_fetch_sub, ~, ord, a, subtrahend);) \ - BSON_IF_GNU_LEGACY_ATOMICS (DEF_ATOMIC_OP (~, ~, __sync_fetch_and_sub, ord, a, subtrahend);) \ - } \ - \ - static BSON_INLINE Type bson_atomic_##NamePart##_fetch (Type volatile const *a, enum bson_memory_order order) \ - { \ - /* MSVC doesn't have a load intrinsic, so just add zero */ \ - BSON_IF_MSVC (return bson_atomic_##NamePart##_fetch_add ((Type volatile *) a, 0, order);) \ - /* GNU doesn't want RELEASE order for the fetch operation, so we can't \ - * just use DEF_ATOMIC_OP. */ \ - BSON_IF_GNU_LIKE (switch (order) { \ - case bson_memory_order_release: /* Fall back to seqcst */ \ - case bson_memory_order_acq_rel: /* Fall back to seqcst */ \ - case bson_memory_order_seq_cst: \ - return __atomic_load_n (a, __ATOMIC_SEQ_CST); \ - case bson_memory_order_acquire: \ - return __atomic_load_n (a, __ATOMIC_ACQUIRE); \ - case bson_memory_order_consume: \ - return __atomic_load_n (a, __ATOMIC_CONSUME); \ - case bson_memory_order_relaxed: \ - return __atomic_load_n (a, __ATOMIC_RELAXED); \ - default: \ - BSON_UNREACHABLE ("Invalid bson_memory_order value"); \ - }) \ - BSON_IF_GNU_LEGACY_ATOMICS ({ \ - __sync_synchronize (); \ - return *a; \ - }) \ - } \ - \ - static BSON_INLINE Type bson_atomic_##NamePart##_exchange ( \ - Type volatile *a, Type value, enum bson_memory_order ord) \ - { \ - BSON_IF_MSVC (DEF_ATOMIC_OP (BSON_CONCAT (_InterlockedExchange, VCIntrinSuffix), ~, ~, ord, a, value);) \ - /* GNU doesn't want CONSUME order for the exchange operation, so we \ - * cannot use DEF_ATOMIC_OP. */ \ - BSON_IF_GNU_LIKE (switch (ord) { \ - case bson_memory_order_acq_rel: \ - return __atomic_exchange_n (a, value, __ATOMIC_ACQ_REL); \ - case bson_memory_order_release: \ - return __atomic_exchange_n (a, value, __ATOMIC_RELEASE); \ - case bson_memory_order_seq_cst: \ - return __atomic_exchange_n (a, value, __ATOMIC_SEQ_CST); \ - case bson_memory_order_consume: /* Fall back to acquire */ \ - case bson_memory_order_acquire: \ - return __atomic_exchange_n (a, value, __ATOMIC_ACQUIRE); \ - case bson_memory_order_relaxed: \ - return __atomic_exchange_n (a, value, __ATOMIC_RELAXED); \ - default: \ - BSON_UNREACHABLE ("Invalid bson_memory_order value"); \ - }) \ - BSON_IF_GNU_LEGACY_ATOMICS (return __sync_val_compare_and_swap (a, *a, value);) \ - } \ - \ - static BSON_INLINE Type bson_atomic_##NamePart##_compare_exchange_strong ( \ - Type volatile *a, Type expect, Type new_value, enum bson_memory_order ord) \ - { \ - Type actual = expect; \ - switch (ord) { \ - case bson_memory_order_release: \ - case bson_memory_order_acq_rel: \ - case bson_memory_order_seq_cst: \ - DEF_ATOMIC_CMPEXCH_STRONG (VCIntrinSuffix, , __ATOMIC_SEQ_CST, a, actual, new_value); \ - break; \ - case bson_memory_order_acquire: \ - DEF_ATOMIC_CMPEXCH_STRONG ( \ - VCIntrinSuffix, MSVC_MEMORDER_SUFFIX (_acq), __ATOMIC_ACQUIRE, a, actual, new_value); \ - break; \ - case bson_memory_order_consume: \ - DEF_ATOMIC_CMPEXCH_STRONG ( \ - VCIntrinSuffix, MSVC_MEMORDER_SUFFIX (_acq), __ATOMIC_CONSUME, a, actual, new_value); \ - break; \ - case bson_memory_order_relaxed: \ - DEF_ATOMIC_CMPEXCH_STRONG ( \ - VCIntrinSuffix, MSVC_MEMORDER_SUFFIX (_nf), __ATOMIC_RELAXED, a, actual, new_value); \ - break; \ - default: \ - BSON_UNREACHABLE ("Invalid bson_memory_order value"); \ - } \ - return actual; \ - } \ - \ - static BSON_INLINE Type bson_atomic_##NamePart##_compare_exchange_weak ( \ - Type volatile *a, Type expect, Type new_value, enum bson_memory_order ord) \ - { \ - Type actual = expect; \ - switch (ord) { \ - case bson_memory_order_release: \ - case bson_memory_order_acq_rel: \ - case bson_memory_order_seq_cst: \ - DEF_ATOMIC_CMPEXCH_WEAK (VCIntrinSuffix, , __ATOMIC_SEQ_CST, a, actual, new_value); \ - break; \ - case bson_memory_order_acquire: \ - DEF_ATOMIC_CMPEXCH_WEAK ( \ - VCIntrinSuffix, MSVC_MEMORDER_SUFFIX (_acq), __ATOMIC_ACQUIRE, a, actual, new_value); \ - break; \ - case bson_memory_order_consume: \ - DEF_ATOMIC_CMPEXCH_WEAK ( \ - VCIntrinSuffix, MSVC_MEMORDER_SUFFIX (_acq), __ATOMIC_CONSUME, a, actual, new_value); \ - break; \ - case bson_memory_order_relaxed: \ - DEF_ATOMIC_CMPEXCH_WEAK (VCIntrinSuffix, MSVC_MEMORDER_SUFFIX (_nf), __ATOMIC_RELAXED, a, actual, new_value); \ - break; \ - default: \ - BSON_UNREACHABLE ("Invalid bson_memory_order value"); \ - } \ - return actual; \ - } - -#define DECL_ATOMIC_STDINT(Name, VCSuffix) DECL_ATOMIC_INTEGRAL (Name, Name##_t, VCSuffix) - -#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, DECL_ATOMIC_INTEGRAL_INT32, ) -#endif -#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) -DECL_ATOMIC_STDINT (int32, ) -#endif -#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, enum bson_memory_order); -BSON_EXPORT (int64_t) -_bson_emul_atomic_int64_exchange (int64_t volatile *val, int64_t v, enum bson_memory_order); -BSON_EXPORT (int64_t) -_bson_emul_atomic_int64_compare_exchange_strong (int64_t volatile *val, - int64_t expect_value, - int64_t new_value, - enum bson_memory_order); - -BSON_EXPORT (int64_t) -_bson_emul_atomic_int64_compare_exchange_weak (int64_t volatile *val, - int64_t expect_value, - int64_t new_value, - enum bson_memory_order); - -BSON_EXPORT (int32_t) -_bson_emul_atomic_int32_fetch_add (int32_t volatile *val, int32_t v, enum bson_memory_order); -BSON_EXPORT (int32_t) -_bson_emul_atomic_int32_exchange (int32_t volatile *val, int32_t v, enum bson_memory_order); -BSON_EXPORT (int32_t) -_bson_emul_atomic_int32_compare_exchange_strong (int32_t volatile *val, - int32_t expect_value, - int32_t new_value, - enum bson_memory_order); - -BSON_EXPORT (int32_t) -_bson_emul_atomic_int32_compare_exchange_weak (int32_t volatile *val, - int32_t expect_value, - int32_t new_value, - enum bson_memory_order); - -BSON_EXPORT (int) -_bson_emul_atomic_int_fetch_add (int volatile *val, int v, enum bson_memory_order); -BSON_EXPORT (int) -_bson_emul_atomic_int_exchange (int volatile *val, int v, enum bson_memory_order); -BSON_EXPORT (int) -_bson_emul_atomic_int_compare_exchange_strong (int volatile *val, - int expect_value, - int new_value, - enum bson_memory_order); - -BSON_EXPORT (int) -_bson_emul_atomic_int_compare_exchange_weak (int volatile *val, - int expect_value, - 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); - -#if (defined(_MSC_VER) && !defined(_M_IX86)) || (defined(__LP64__) && __LP64__) -/* (64-bit intrinsics are only available in x64) */ -#ifdef _MSC_VER -DECL_ATOMIC_INTEGRAL (int64, __int64, 64) -#else -DECL_ATOMIC_STDINT (int64, 64) -#endif -#else -static BSON_INLINE int64_t -bson_atomic_int64_fetch (const int64_t volatile *val, enum bson_memory_order order) -{ - return _bson_emul_atomic_int64_fetch_add ((int64_t volatile *) val, 0, order); -} - -static BSON_INLINE int64_t -bson_atomic_int64_fetch_add (int64_t volatile *val, int64_t v, enum bson_memory_order order) -{ - return _bson_emul_atomic_int64_fetch_add (val, v, order); -} - -static BSON_INLINE int64_t -bson_atomic_int64_fetch_sub (int64_t volatile *val, int64_t v, enum bson_memory_order order) -{ - return _bson_emul_atomic_int64_fetch_add (val, -v, order); -} - -static BSON_INLINE int64_t -bson_atomic_int64_exchange (int64_t volatile *val, int64_t v, enum bson_memory_order order) -{ - return _bson_emul_atomic_int64_exchange (val, v, order); -} - -static BSON_INLINE int64_t -bson_atomic_int64_compare_exchange_strong (int64_t volatile *val, - int64_t expect_value, - int64_t new_value, - enum bson_memory_order order) -{ - return _bson_emul_atomic_int64_compare_exchange_strong (val, expect_value, new_value, order); -} - -static BSON_INLINE int64_t -bson_atomic_int64_compare_exchange_weak (int64_t volatile *val, - int64_t expect_value, - int64_t new_value, - enum bson_memory_order order) -{ - return _bson_emul_atomic_int64_compare_exchange_weak (val, expect_value, new_value, order); -} -#endif - -#if defined(BSON_EMULATE_INT32) -static BSON_INLINE int32_t -bson_atomic_int32_fetch (const int32_t volatile *val, enum bson_memory_order order) -{ - return _bson_emul_atomic_int32_fetch_add ((int32_t volatile *) val, 0, order); -} - -static BSON_INLINE int32_t -bson_atomic_int32_fetch_add (int32_t volatile *val, int32_t v, enum bson_memory_order order) -{ - return _bson_emul_atomic_int32_fetch_add (val, v, order); -} - -static BSON_INLINE int32_t -bson_atomic_int32_fetch_sub (int32_t volatile *val, int32_t v, enum bson_memory_order order) -{ - return _bson_emul_atomic_int32_fetch_add (val, -v, order); -} - -static BSON_INLINE int32_t -bson_atomic_int32_exchange (int32_t volatile *val, int32_t v, enum bson_memory_order order) -{ - return _bson_emul_atomic_int32_exchange (val, v, order); -} - -static BSON_INLINE int32_t -bson_atomic_int32_compare_exchange_strong (int32_t volatile *val, - int32_t expect_value, - int32_t new_value, - enum bson_memory_order order) -{ - return _bson_emul_atomic_int32_compare_exchange_strong (val, expect_value, new_value, order); -} - -static BSON_INLINE int32_t -bson_atomic_int32_compare_exchange_weak (int32_t volatile *val, - int32_t expect_value, - int32_t new_value, - enum bson_memory_order order) -{ - return _bson_emul_atomic_int32_compare_exchange_weak (val, expect_value, new_value, order); -} -#endif /* BSON_EMULATE_INT32 */ - -#if defined(BSON_EMULATE_INT) -static BSON_INLINE int -bson_atomic_int_fetch (const int volatile *val, enum bson_memory_order order) -{ - return _bson_emul_atomic_int_fetch_add ((int volatile *) val, 0, order); -} - -static BSON_INLINE int -bson_atomic_int_fetch_add (int volatile *val, int v, enum bson_memory_order order) -{ - return _bson_emul_atomic_int_fetch_add (val, v, order); -} - -static BSON_INLINE int -bson_atomic_int_fetch_sub (int volatile *val, int v, enum bson_memory_order order) -{ - return _bson_emul_atomic_int_fetch_add (val, -v, order); -} - -static BSON_INLINE int -bson_atomic_int_exchange (int volatile *val, int v, enum bson_memory_order order) -{ - return _bson_emul_atomic_int_exchange (val, v, order); -} - -static BSON_INLINE int -bson_atomic_int_compare_exchange_strong (int volatile *val, - int expect_value, - int new_value, - enum bson_memory_order order) -{ - return _bson_emul_atomic_int_compare_exchange_strong (val, expect_value, new_value, order); -} - -static BSON_INLINE int -bson_atomic_int_compare_exchange_weak (int volatile *val, int expect_value, int new_value, enum bson_memory_order order) -{ - return _bson_emul_atomic_int_compare_exchange_weak (val, expect_value, new_value, order); -} -#endif /* BSON_EMULATE_INT */ - -static BSON_INLINE void * -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 */ - DEF_ATOMIC_OP (_InterlockedExchangePointer, , __sync_val_compare_and_swap, ord, ptr, *ptr, new_value); -#else - DEF_ATOMIC_OP (_InterlockedExchangePointer, __atomic_exchange_n, , ord, ptr, new_value); -#endif -} - -static BSON_INLINE void * -bson_atomic_ptr_compare_exchange_strong (void *volatile *ptr, void *expect, void *new_value, enum bson_memory_order ord) -{ - switch (ord) { - case bson_memory_order_release: - case bson_memory_order_acq_rel: - case bson_memory_order_seq_cst: - DEF_ATOMIC_CMPEXCH_STRONG (Pointer, , __ATOMIC_SEQ_CST, ptr, expect, new_value); - return expect; - case bson_memory_order_relaxed: - DEF_ATOMIC_CMPEXCH_STRONG (Pointer, MSVC_MEMORDER_SUFFIX (_nf), __ATOMIC_RELAXED, ptr, expect, new_value); - return expect; - case bson_memory_order_consume: - DEF_ATOMIC_CMPEXCH_STRONG (Pointer, MSVC_MEMORDER_SUFFIX (_acq), __ATOMIC_CONSUME, ptr, expect, new_value); - return expect; - case bson_memory_order_acquire: - DEF_ATOMIC_CMPEXCH_STRONG (Pointer, MSVC_MEMORDER_SUFFIX (_acq), __ATOMIC_ACQUIRE, ptr, expect, new_value); - return expect; - default: - BSON_UNREACHABLE ("Invalid bson_memory_order value"); - } -} - - -static BSON_INLINE void * -bson_atomic_ptr_compare_exchange_weak (void *volatile *ptr, void *expect, void *new_value, enum bson_memory_order ord) -{ - switch (ord) { - case bson_memory_order_release: - case bson_memory_order_acq_rel: - case bson_memory_order_seq_cst: - DEF_ATOMIC_CMPEXCH_WEAK (Pointer, , __ATOMIC_SEQ_CST, ptr, expect, new_value); - return expect; - case bson_memory_order_relaxed: - DEF_ATOMIC_CMPEXCH_WEAK (Pointer, MSVC_MEMORDER_SUFFIX (_nf), __ATOMIC_RELAXED, ptr, expect, new_value); - return expect; - case bson_memory_order_consume: - DEF_ATOMIC_CMPEXCH_WEAK (Pointer, MSVC_MEMORDER_SUFFIX (_acq), __ATOMIC_CONSUME, ptr, expect, new_value); - return expect; - case bson_memory_order_acquire: - DEF_ATOMIC_CMPEXCH_WEAK (Pointer, MSVC_MEMORDER_SUFFIX (_acq), __ATOMIC_ACQUIRE, ptr, expect, new_value); - return expect; - default: - BSON_UNREACHABLE ("Invalid bson_memory_order value"); - } -} - - -static BSON_INLINE void * -bson_atomic_ptr_fetch (void *volatile const *ptr, enum bson_memory_order ord) -{ - return bson_atomic_ptr_compare_exchange_strong ((void *volatile *) ptr, NULL, NULL, ord); -} - -#undef DECL_ATOMIC_STDINT -#undef DECL_ATOMIC_INTEGRAL -#undef DEF_ATOMIC_OP -#undef DEF_ATOMIC_CMPEXCH_STRONG -#undef DEF_ATOMIC_CMPEXCH_WEAK -#undef MSVC_MEMORDER_SUFFIX - -/** - * @brief Generate a full-fence memory barrier at the call site. - */ -static BSON_INLINE void -bson_atomic_thread_fence (void) -{ - BSON_IF_MSVC (MemoryBarrier ();) - BSON_IF_GNU_LIKE (__sync_synchronize ();) - BSON_IF_GNU_LEGACY_ATOMICS (__sync_synchronize ();) -} - -#ifdef BSON_USE_LEGACY_GCC_ATOMICS -#undef BSON_IF_GNU_LIKE -#define BSON_IF_GNU_LIKE(...) __VA_ARGS__ -#endif -#undef BSON_IF_GNU_LEGACY_ATOMICS -#undef BSON_USE_LEGACY_GCC_ATOMICS - -BSON_GNUC_DEPRECATED_FOR ("bson_atomic_thread_fence") -BSON_EXPORT (void) bson_memory_barrier (void); - -BSON_GNUC_DEPRECATED_FOR ("bson_atomic_int_fetch_add") -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 - -BSON_END_DECLS - - -#endif /* BSON_ATOMIC_H */ diff --git a/bsonjs/bson/bcon.c b/bsonjs/bson/bson-bcon.c similarity index 56% rename from bsonjs/bson/bcon.c rename to bsonjs/bson/bson-bcon.c index 0779d89..503a911 100644 --- a/bsonjs/bson/bcon.c +++ b/bsonjs/bson/bson-bcon.c @@ -3,7 +3,7 @@ * @brief BCON (BSON C Object Notation) Implementation */ -/* Copyright 2009-2013 MongoDB, Inc. +/* Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,10 +19,11 @@ */ -#include +#include + +#include -#include "bcon.h" -#include +#include /* These stack manipulation macros are used to manage append recursion in * bcon_append_ctx_va(). They take care of some awkward dereference rules (the @@ -31,52 +32,51 @@ #define STACK_ELE(_delta, _name) (ctx->stack[(_delta) + ctx->n]._name) -#define STACK_BSON(_delta) (((_delta) + ctx->n) == 0 ? bson : &STACK_ELE (_delta, bson)) +#define STACK_BSON(_delta) (((_delta) + ctx->n) == 0 ? bson : &STACK_ELE(_delta, bson)) -#define STACK_ITER(_delta) (((_delta) + ctx->n) == 0 ? &root_iter : &STACK_ELE (_delta, iter)) +#define STACK_ITER(_delta) (((_delta) + ctx->n) == 0 ? &root_iter : &STACK_ELE(_delta, iter)) -#define STACK_BSON_PARENT STACK_BSON (-1) -#define STACK_BSON_CHILD STACK_BSON (0) +#define STACK_BSON_PARENT STACK_BSON(-1) +#define STACK_BSON_CHILD STACK_BSON(0) -#define STACK_ITER_PARENT STACK_ITER (-1) -#define STACK_ITER_CHILD STACK_ITER (0) +#define STACK_ITER_CHILD STACK_ITER(0) -#define STACK_I STACK_ELE (0, i) -#define STACK_IS_ARRAY STACK_ELE (0, is_array) +#define STACK_I STACK_ELE(0, i) +#define STACK_IS_ARRAY STACK_ELE(0, is_array) -#define STACK_PUSH_ARRAY(statement) \ - do { \ - BSON_ASSERT (ctx->n < (BCON_STACK_MAX - 1)); \ - ctx->n++; \ - STACK_I = 0; \ - STACK_IS_ARRAY = 1; \ - statement; \ +#define STACK_PUSH_ARRAY(statement) \ + do { \ + BSON_ASSERT(ctx->n < (BCON_STACK_MAX - 1)); \ + ctx->n++; \ + STACK_I = 0; \ + STACK_IS_ARRAY = 1; \ + statement; \ } while (0) -#define STACK_PUSH_DOC(statement) \ - do { \ - BSON_ASSERT (ctx->n < (BCON_STACK_MAX - 1)); \ - ctx->n++; \ - STACK_IS_ARRAY = 0; \ - statement; \ +#define STACK_PUSH_DOC(statement) \ + do { \ + BSON_ASSERT(ctx->n < (BCON_STACK_MAX - 1)); \ + ctx->n++; \ + STACK_IS_ARRAY = 0; \ + statement; \ } while (0) -#define STACK_POP_ARRAY(statement) \ +#define STACK_POP_ARRAY(statement) \ + do { \ + BSON_ASSERT(STACK_IS_ARRAY); \ + BSON_ASSERT(ctx->n != 0); \ + statement; \ + ctx->n--; \ + } while (0) + +#define STACK_POP_DOC(statement) \ do { \ - BSON_ASSERT (STACK_IS_ARRAY); \ - BSON_ASSERT (ctx->n != 0); \ + BSON_ASSERT(!STACK_IS_ARRAY); \ + BSON_ASSERT(ctx->n != 0); \ statement; \ ctx->n--; \ } while (0) -#define STACK_POP_DOC(statement) \ - do { \ - BSON_ASSERT (!STACK_IS_ARRAY); \ - BSON_ASSERT (ctx->n != 0); \ - statement; \ - ctx->n--; \ - } while (0) - /* This is a landing pad union for all of the types we can process with bcon. * We need actual storage for this to capture the return value of va_arg, which * takes multiple calls to get everything we need for some complex types */ @@ -183,112 +183,112 @@ static const char *gBconMagic = "BCON_MAGIC"; static const char *gBconeMagic = "BCONE_MAGIC"; const char * -bson_bcon_magic (void) +bson_bcon_magic(void) { return gBconMagic; } const char * -bson_bcone_magic (void) +bson_bcone_magic(void) { return gBconeMagic; } static void -_noop (void) +_noop(void) { } /* appends val to the passed bson object. Meant to be a super simple dispatch * table */ static void -_bcon_append_single (bson_t *bson, bcon_type_t type, const char *key, bcon_append_t *val) +_bcon_append_single(bson_t *bson, bcon_type_t type, const char *key, bcon_append_t *val) { - switch ((int) type) { + switch ((int)type) { case BCON_TYPE_UTF8: - BSON_ASSERT (bson_append_utf8 (bson, key, -1, val->UTF8, -1)); + BSON_ASSERT(bson_append_utf8(bson, key, -1, val->UTF8, -1)); break; case BCON_TYPE_DOUBLE: - BSON_ASSERT (bson_append_double (bson, key, -1, val->DOUBLE)); + BSON_ASSERT(bson_append_double(bson, key, -1, val->DOUBLE)); break; case BCON_TYPE_BIN: { - BSON_ASSERT (bson_append_binary (bson, key, -1, val->BIN.subtype, val->BIN.binary, val->BIN.length)); + BSON_ASSERT(bson_append_binary(bson, key, -1, val->BIN.subtype, val->BIN.binary, val->BIN.length)); break; } case BCON_TYPE_UNDEFINED: - BSON_ASSERT (bson_append_undefined (bson, key, -1)); + BSON_ASSERT(bson_append_undefined(bson, key, -1)); break; case BCON_TYPE_OID: - BSON_ASSERT (bson_append_oid (bson, key, -1, val->OID)); + BSON_ASSERT(bson_append_oid(bson, key, -1, val->OID)); break; case BCON_TYPE_BOOL: - BSON_ASSERT (bson_append_bool (bson, key, -1, (bool) val->BOOL)); + BSON_ASSERT(bson_append_bool(bson, key, -1, (bool)val->BOOL)); break; case BCON_TYPE_DATE_TIME: - BSON_ASSERT (bson_append_date_time (bson, key, -1, val->DATE_TIME)); + BSON_ASSERT(bson_append_date_time(bson, key, -1, val->DATE_TIME)); break; case BCON_TYPE_NULL: - BSON_ASSERT (bson_append_null (bson, key, -1)); + BSON_ASSERT(bson_append_null(bson, key, -1)); break; case BCON_TYPE_REGEX: { - BSON_ASSERT (bson_append_regex (bson, key, -1, val->REGEX.regex, val->REGEX.flags)); + BSON_ASSERT(bson_append_regex(bson, key, -1, val->REGEX.regex, val->REGEX.flags)); break; } case BCON_TYPE_DBPOINTER: { - BSON_ASSERT (bson_append_dbpointer (bson, key, -1, val->DBPOINTER.collection, val->DBPOINTER.oid)); + BSON_ASSERT(bson_append_dbpointer(bson, key, -1, val->DBPOINTER.collection, val->DBPOINTER.oid)); break; } case BCON_TYPE_CODE: - BSON_ASSERT (bson_append_code (bson, key, -1, val->CODE)); + BSON_ASSERT(bson_append_code(bson, key, -1, val->CODE)); break; case BCON_TYPE_SYMBOL: - BSON_ASSERT (bson_append_symbol (bson, key, -1, val->SYMBOL, -1)); + BSON_ASSERT(bson_append_symbol(bson, key, -1, val->SYMBOL, -1)); break; case BCON_TYPE_CODEWSCOPE: - BSON_ASSERT (bson_append_code_with_scope (bson, key, -1, val->CODEWSCOPE.js, val->CODEWSCOPE.scope)); + BSON_ASSERT(bson_append_code_with_scope(bson, key, -1, val->CODEWSCOPE.js, val->CODEWSCOPE.scope)); break; case BCON_TYPE_INT32: - BSON_ASSERT (bson_append_int32 (bson, key, -1, val->INT32)); + BSON_ASSERT(bson_append_int32(bson, key, -1, val->INT32)); break; case BCON_TYPE_TIMESTAMP: { - BSON_ASSERT (bson_append_timestamp (bson, key, -1, val->TIMESTAMP.timestamp, val->TIMESTAMP.increment)); + BSON_ASSERT(bson_append_timestamp(bson, key, -1, val->TIMESTAMP.timestamp, val->TIMESTAMP.increment)); break; } case BCON_TYPE_INT64: - BSON_ASSERT (bson_append_int64 (bson, key, -1, val->INT64)); + BSON_ASSERT(bson_append_int64(bson, key, -1, val->INT64)); break; case BCON_TYPE_DECIMAL128: - BSON_ASSERT (bson_append_decimal128 (bson, key, -1, val->DECIMAL128)); + BSON_ASSERT(bson_append_decimal128(bson, key, -1, val->DECIMAL128)); break; case BCON_TYPE_MAXKEY: - BSON_ASSERT (bson_append_maxkey (bson, key, -1)); + BSON_ASSERT(bson_append_maxkey(bson, key, -1)); break; case BCON_TYPE_MINKEY: - BSON_ASSERT (bson_append_minkey (bson, key, -1)); + BSON_ASSERT(bson_append_minkey(bson, key, -1)); break; case BCON_TYPE_ARRAY: { - BSON_ASSERT (bson_append_array (bson, key, -1, val->ARRAY)); + BSON_ASSERT(bson_append_array(bson, key, -1, val->ARRAY)); break; } case BCON_TYPE_DOCUMENT: { - BSON_ASSERT (bson_append_document (bson, key, -1, val->DOCUMENT)); + BSON_ASSERT(bson_append_document(bson, key, -1, val->DOCUMENT)); break; } case BCON_TYPE_ITER: - BSON_ASSERT (bson_append_iter (bson, key, -1, val->ITER)); + BSON_ASSERT(bson_append_iter(bson, key, -1, val->ITER)); break; default: - BSON_ASSERT (0); + BSON_ASSERT(0); break; } } -#define CHECK_TYPE(_type) \ - do { \ - if (bson_iter_type (iter) != (_type)) { \ - return false; \ - } \ +#define CHECK_TYPE(_type) \ + do { \ + if (bson_iter_type(iter) != (_type)) { \ + return false; \ + } \ } while (0) /* extracts the value under the iterator and writes it to val. returns false @@ -305,119 +305,119 @@ _bcon_append_single (bson_t *bson, bcon_type_t type, const char *key, bcon_appen * procedural verification (if a parameter could have multiple types). * */ static bool -_bcon_extract_single (const bson_iter_t *iter, bcon_type_t type, bcon_extract_t *val) +_bcon_extract_single(const bson_iter_t *iter, bcon_type_t type, bcon_extract_t *val) { - switch ((int) type) { + switch ((int)type) { case BCON_TYPE_UTF8: - CHECK_TYPE (BSON_TYPE_UTF8); - *val->UTF8 = bson_iter_utf8 (iter, NULL); + CHECK_TYPE(BSON_TYPE_UTF8); + *val->UTF8 = bson_iter_utf8(iter, NULL); break; case BCON_TYPE_DOUBLE: - CHECK_TYPE (BSON_TYPE_DOUBLE); - *val->DOUBLE = bson_iter_double (iter); + CHECK_TYPE(BSON_TYPE_DOUBLE); + *val->DOUBLE = bson_iter_double(iter); break; case BCON_TYPE_BIN: - CHECK_TYPE (BSON_TYPE_BINARY); - bson_iter_binary (iter, val->BIN.subtype, val->BIN.length, val->BIN.binary); + CHECK_TYPE(BSON_TYPE_BINARY); + bson_iter_binary(iter, val->BIN.subtype, val->BIN.length, val->BIN.binary); break; case BCON_TYPE_UNDEFINED: - CHECK_TYPE (BSON_TYPE_UNDEFINED); + CHECK_TYPE(BSON_TYPE_UNDEFINED); break; case BCON_TYPE_OID: - CHECK_TYPE (BSON_TYPE_OID); - *val->OID = bson_iter_oid (iter); + CHECK_TYPE(BSON_TYPE_OID); + *val->OID = bson_iter_oid(iter); break; case BCON_TYPE_BOOL: - CHECK_TYPE (BSON_TYPE_BOOL); - *val->BOOL = bson_iter_bool (iter); + CHECK_TYPE(BSON_TYPE_BOOL); + *val->BOOL = bson_iter_bool(iter); break; case BCON_TYPE_DATE_TIME: - CHECK_TYPE (BSON_TYPE_DATE_TIME); - *val->DATE_TIME = bson_iter_date_time (iter); + CHECK_TYPE(BSON_TYPE_DATE_TIME); + *val->DATE_TIME = bson_iter_date_time(iter); break; case BCON_TYPE_NULL: - CHECK_TYPE (BSON_TYPE_NULL); + CHECK_TYPE(BSON_TYPE_NULL); break; case BCON_TYPE_REGEX: - CHECK_TYPE (BSON_TYPE_REGEX); - *val->REGEX.regex = bson_iter_regex (iter, val->REGEX.flags); + CHECK_TYPE(BSON_TYPE_REGEX); + *val->REGEX.regex = bson_iter_regex(iter, val->REGEX.flags); break; case BCON_TYPE_DBPOINTER: - CHECK_TYPE (BSON_TYPE_DBPOINTER); - bson_iter_dbpointer (iter, NULL, val->DBPOINTER.collection, val->DBPOINTER.oid); + CHECK_TYPE(BSON_TYPE_DBPOINTER); + bson_iter_dbpointer(iter, NULL, val->DBPOINTER.collection, val->DBPOINTER.oid); break; case BCON_TYPE_CODE: - CHECK_TYPE (BSON_TYPE_CODE); - *val->CODE = bson_iter_code (iter, NULL); + CHECK_TYPE(BSON_TYPE_CODE); + *val->CODE = bson_iter_code(iter, NULL); break; case BCON_TYPE_SYMBOL: - CHECK_TYPE (BSON_TYPE_SYMBOL); - *val->SYMBOL = bson_iter_symbol (iter, NULL); + CHECK_TYPE(BSON_TYPE_SYMBOL); + *val->SYMBOL = bson_iter_symbol(iter, NULL); break; case BCON_TYPE_CODEWSCOPE: { const uint8_t *buf; uint32_t len; - CHECK_TYPE (BSON_TYPE_CODEWSCOPE); + CHECK_TYPE(BSON_TYPE_CODEWSCOPE); - *val->CODEWSCOPE.js = bson_iter_codewscope (iter, NULL, &len, &buf); + *val->CODEWSCOPE.js = bson_iter_codewscope(iter, NULL, &len, &buf); - BSON_ASSERT (bson_init_static (val->CODEWSCOPE.scope, buf, len)); + BSON_ASSERT(bson_init_static(val->CODEWSCOPE.scope, buf, len)); break; } case BCON_TYPE_INT32: - CHECK_TYPE (BSON_TYPE_INT32); - *val->INT32 = bson_iter_int32 (iter); + CHECK_TYPE(BSON_TYPE_INT32); + *val->INT32 = bson_iter_int32(iter); break; case BCON_TYPE_TIMESTAMP: - CHECK_TYPE (BSON_TYPE_TIMESTAMP); - bson_iter_timestamp (iter, val->TIMESTAMP.timestamp, val->TIMESTAMP.increment); + CHECK_TYPE(BSON_TYPE_TIMESTAMP); + bson_iter_timestamp(iter, val->TIMESTAMP.timestamp, val->TIMESTAMP.increment); break; case BCON_TYPE_INT64: - CHECK_TYPE (BSON_TYPE_INT64); - *val->INT64 = bson_iter_int64 (iter); + CHECK_TYPE(BSON_TYPE_INT64); + *val->INT64 = bson_iter_int64(iter); break; case BCON_TYPE_DECIMAL128: - CHECK_TYPE (BSON_TYPE_DECIMAL128); - BSON_ASSERT (bson_iter_decimal128 (iter, val->DECIMAL128)); + CHECK_TYPE(BSON_TYPE_DECIMAL128); + BSON_ASSERT(bson_iter_decimal128(iter, val->DECIMAL128)); break; case BCON_TYPE_MAXKEY: - CHECK_TYPE (BSON_TYPE_MAXKEY); + CHECK_TYPE(BSON_TYPE_MAXKEY); break; case BCON_TYPE_MINKEY: - CHECK_TYPE (BSON_TYPE_MINKEY); + CHECK_TYPE(BSON_TYPE_MINKEY); break; case BCON_TYPE_ARRAY: { const uint8_t *buf; uint32_t len; - CHECK_TYPE (BSON_TYPE_ARRAY); + CHECK_TYPE(BSON_TYPE_ARRAY); - bson_iter_array (iter, &len, &buf); + bson_iter_array(iter, &len, &buf); - BSON_ASSERT (bson_init_static (val->ARRAY, buf, len)); + BSON_ASSERT(bson_init_static(val->ARRAY, buf, len)); break; } case BCON_TYPE_DOCUMENT: { const uint8_t *buf; uint32_t len; - CHECK_TYPE (BSON_TYPE_DOCUMENT); + CHECK_TYPE(BSON_TYPE_DOCUMENT); - bson_iter_document (iter, &len, &buf); + bson_iter_document(iter, &len, &buf); - BSON_ASSERT (bson_init_static (val->DOCUMENT, buf, len)); + BSON_ASSERT(bson_init_static(val->DOCUMENT, buf, len)); break; } case BCON_TYPE_SKIP: - CHECK_TYPE (val->TYPE); + CHECK_TYPE(val->TYPE); break; case BCON_TYPE_ITER: - memcpy (val->ITER, iter, sizeof *iter); + memcpy(val->ITER, iter, sizeof *iter); break; default: - BSON_ASSERT (0); + BSON_ASSERT(0); break; } @@ -440,94 +440,94 @@ _bcon_extract_single (const bson_iter_t *iter, bcon_type_t type, bcon_extract_t * II. If not, just call it a UTF8 token and pass that back */ static bcon_type_t -_bcon_append_tokenize (va_list *ap, bcon_append_t *u) +_bcon_append_tokenize(va_list *ap, bcon_append_t *u) { char *mark; bcon_type_t type; - mark = va_arg (*ap, char *); + mark = va_arg(*ap, char *); - BSON_ASSERT (mark != BCONE_MAGIC); + BSON_ASSERT(mark != BCONE_MAGIC); if (mark == NULL) { type = BCON_TYPE_END; } else if (mark == BCON_MAGIC) { - type = va_arg (*ap, bcon_type_t); + type = va_arg(*ap, bcon_type_t); - switch ((int) type) { + switch ((int)type) { case BCON_TYPE_UTF8: - u->UTF8 = va_arg (*ap, char *); + u->UTF8 = va_arg(*ap, char *); break; case BCON_TYPE_DOUBLE: - u->DOUBLE = va_arg (*ap, double); + u->DOUBLE = va_arg(*ap, double); break; case BCON_TYPE_DOCUMENT: - u->DOCUMENT = va_arg (*ap, bson_t *); + u->DOCUMENT = va_arg(*ap, bson_t *); break; case BCON_TYPE_ARRAY: - u->ARRAY = va_arg (*ap, bson_t *); + u->ARRAY = va_arg(*ap, bson_t *); break; case BCON_TYPE_BIN: - u->BIN.subtype = va_arg (*ap, bson_subtype_t); - u->BIN.binary = va_arg (*ap, uint8_t *); - u->BIN.length = va_arg (*ap, uint32_t); + u->BIN.subtype = va_arg(*ap, bson_subtype_t); + u->BIN.binary = va_arg(*ap, uint8_t *); + u->BIN.length = va_arg(*ap, uint32_t); break; case BCON_TYPE_UNDEFINED: break; case BCON_TYPE_OID: - u->OID = va_arg (*ap, bson_oid_t *); + u->OID = va_arg(*ap, bson_oid_t *); break; case BCON_TYPE_BOOL: - u->BOOL = va_arg (*ap, int); + u->BOOL = va_arg(*ap, int); break; case BCON_TYPE_DATE_TIME: - u->DATE_TIME = va_arg (*ap, int64_t); + u->DATE_TIME = va_arg(*ap, int64_t); break; case BCON_TYPE_NULL: break; case BCON_TYPE_REGEX: - u->REGEX.regex = va_arg (*ap, char *); - u->REGEX.flags = va_arg (*ap, char *); + u->REGEX.regex = va_arg(*ap, char *); + u->REGEX.flags = va_arg(*ap, char *); break; case BCON_TYPE_DBPOINTER: - u->DBPOINTER.collection = va_arg (*ap, char *); - u->DBPOINTER.oid = va_arg (*ap, bson_oid_t *); + u->DBPOINTER.collection = va_arg(*ap, char *); + u->DBPOINTER.oid = va_arg(*ap, bson_oid_t *); break; case BCON_TYPE_CODE: - u->CODE = va_arg (*ap, char *); + u->CODE = va_arg(*ap, char *); break; case BCON_TYPE_SYMBOL: - u->SYMBOL = va_arg (*ap, char *); + u->SYMBOL = va_arg(*ap, char *); break; case BCON_TYPE_CODEWSCOPE: - u->CODEWSCOPE.js = va_arg (*ap, char *); - u->CODEWSCOPE.scope = va_arg (*ap, bson_t *); + u->CODEWSCOPE.js = va_arg(*ap, char *); + u->CODEWSCOPE.scope = va_arg(*ap, bson_t *); break; case BCON_TYPE_INT32: - u->INT32 = va_arg (*ap, int32_t); + u->INT32 = va_arg(*ap, int32_t); break; case BCON_TYPE_TIMESTAMP: - u->TIMESTAMP.timestamp = va_arg (*ap, uint32_t); - u->TIMESTAMP.increment = va_arg (*ap, uint32_t); + u->TIMESTAMP.timestamp = va_arg(*ap, uint32_t); + u->TIMESTAMP.increment = va_arg(*ap, uint32_t); break; case BCON_TYPE_INT64: - u->INT64 = va_arg (*ap, int64_t); + u->INT64 = va_arg(*ap, int64_t); break; case BCON_TYPE_DECIMAL128: - u->DECIMAL128 = va_arg (*ap, bson_decimal128_t *); + u->DECIMAL128 = va_arg(*ap, bson_decimal128_t *); break; case BCON_TYPE_MAXKEY: break; case BCON_TYPE_MINKEY: break; case BCON_TYPE_BCON: - u->BCON = va_arg (*ap, bson_t *); + u->BCON = va_arg(*ap, bson_t *); break; case BCON_TYPE_ITER: - u->ITER = va_arg (*ap, const bson_iter_t *); + u->ITER = va_arg(*ap, const bson_iter_t *); break; default: - BSON_ASSERT (0); + BSON_ASSERT(0); break; } } else { @@ -572,94 +572,94 @@ _bcon_append_tokenize (va_list *ap, bcon_append_t *u) * II. If not, just call it a UTF8 token and pass that back */ static bcon_type_t -_bcon_extract_tokenize (va_list *ap, bcon_extract_t *u) +_bcon_extract_tokenize(va_list *ap, bcon_extract_t *u) { char *mark; bcon_type_t type; - mark = va_arg (*ap, char *); + mark = va_arg(*ap, char *); - BSON_ASSERT (mark != BCON_MAGIC); + BSON_ASSERT(mark != BCON_MAGIC); if (mark == NULL) { type = BCON_TYPE_END; } else if (mark == BCONE_MAGIC) { - type = va_arg (*ap, bcon_type_t); + type = va_arg(*ap, bcon_type_t); - switch ((int) type) { + switch ((int)type) { case BCON_TYPE_UTF8: - u->UTF8 = va_arg (*ap, const char **); + u->UTF8 = va_arg(*ap, const char **); break; case BCON_TYPE_DOUBLE: - u->DOUBLE = va_arg (*ap, double *); + u->DOUBLE = va_arg(*ap, double *); break; case BCON_TYPE_DOCUMENT: - u->DOCUMENT = va_arg (*ap, bson_t *); + u->DOCUMENT = va_arg(*ap, bson_t *); break; case BCON_TYPE_ARRAY: - u->ARRAY = va_arg (*ap, bson_t *); + u->ARRAY = va_arg(*ap, bson_t *); break; case BCON_TYPE_BIN: - u->BIN.subtype = va_arg (*ap, bson_subtype_t *); - u->BIN.binary = va_arg (*ap, const uint8_t **); - u->BIN.length = va_arg (*ap, uint32_t *); + u->BIN.subtype = va_arg(*ap, bson_subtype_t *); + u->BIN.binary = va_arg(*ap, const uint8_t **); + u->BIN.length = va_arg(*ap, uint32_t *); break; case BCON_TYPE_UNDEFINED: break; case BCON_TYPE_OID: - u->OID = va_arg (*ap, const bson_oid_t **); + u->OID = va_arg(*ap, const bson_oid_t **); break; case BCON_TYPE_BOOL: - u->BOOL = va_arg (*ap, bool *); + u->BOOL = va_arg(*ap, bool *); break; case BCON_TYPE_DATE_TIME: - u->DATE_TIME = va_arg (*ap, int64_t *); + u->DATE_TIME = va_arg(*ap, int64_t *); break; case BCON_TYPE_NULL: break; case BCON_TYPE_REGEX: - u->REGEX.regex = va_arg (*ap, const char **); - u->REGEX.flags = va_arg (*ap, const char **); + u->REGEX.regex = va_arg(*ap, const char **); + u->REGEX.flags = va_arg(*ap, const char **); break; case BCON_TYPE_DBPOINTER: - u->DBPOINTER.collection = va_arg (*ap, const char **); - u->DBPOINTER.oid = va_arg (*ap, const bson_oid_t **); + u->DBPOINTER.collection = va_arg(*ap, const char **); + u->DBPOINTER.oid = va_arg(*ap, const bson_oid_t **); break; case BCON_TYPE_CODE: - u->CODE = va_arg (*ap, const char **); + u->CODE = va_arg(*ap, const char **); break; case BCON_TYPE_SYMBOL: - u->SYMBOL = va_arg (*ap, const char **); + u->SYMBOL = va_arg(*ap, const char **); break; case BCON_TYPE_CODEWSCOPE: - u->CODEWSCOPE.js = va_arg (*ap, const char **); - u->CODEWSCOPE.scope = va_arg (*ap, bson_t *); + u->CODEWSCOPE.js = va_arg(*ap, const char **); + u->CODEWSCOPE.scope = va_arg(*ap, bson_t *); break; case BCON_TYPE_INT32: - u->INT32 = va_arg (*ap, int32_t *); + u->INT32 = va_arg(*ap, int32_t *); break; case BCON_TYPE_TIMESTAMP: - u->TIMESTAMP.timestamp = va_arg (*ap, uint32_t *); - u->TIMESTAMP.increment = va_arg (*ap, uint32_t *); + u->TIMESTAMP.timestamp = va_arg(*ap, uint32_t *); + u->TIMESTAMP.increment = va_arg(*ap, uint32_t *); break; case BCON_TYPE_INT64: - u->INT64 = va_arg (*ap, int64_t *); + u->INT64 = va_arg(*ap, int64_t *); break; case BCON_TYPE_DECIMAL128: - u->DECIMAL128 = va_arg (*ap, bson_decimal128_t *); + u->DECIMAL128 = va_arg(*ap, bson_decimal128_t *); break; case BCON_TYPE_MAXKEY: break; case BCON_TYPE_MINKEY: break; case BCON_TYPE_SKIP: - u->TYPE = va_arg (*ap, bson_type_t); + u->TYPE = va_arg(*ap, bson_type_t); break; case BCON_TYPE_ITER: - u->ITER = va_arg (*ap, bson_iter_t *); + u->ITER = va_arg(*ap, bson_iter_t *); break; default: - BSON_ASSERT (0); + BSON_ASSERT(0); break; } } else { @@ -693,27 +693,27 @@ _bcon_extract_tokenize (va_list *ap, bcon_extract_t *u) * continuing to use and increment the keys from the source. It's only useful * when called from bcon_append_ctx_va */ static void -_bson_concat_array (bson_t *dest, const bson_t *src, bcon_append_ctx_t *ctx) +_bson_concat_array(bson_t *dest, const bson_t *src, bcon_append_ctx_t *ctx) { bson_iter_t iter; const char *key; char i_str[16]; bool r; - r = bson_iter_init (&iter, src); + r = bson_iter_init(&iter, src); if (!r) { - fprintf (stderr, "Invalid BSON document, possible memory coruption.\n"); + fprintf(stderr, "Invalid BSON document, possible memory coruption.\n"); return; } STACK_I--; - while (bson_iter_next (&iter)) { - bson_uint32_to_string (STACK_I, &key, i_str, sizeof i_str); + while (bson_iter_next(&iter)) { + bson_uint32_to_string(STACK_I, &key, i_str, sizeof i_str); STACK_I++; - BSON_ASSERT (bson_append_iter (dest, key, -1, &iter)); + BSON_ASSERT(bson_append_iter(dest, key, -1, &iter)); } } @@ -737,7 +737,7 @@ _bson_concat_array (bson_t *dest, const bson_t *src, bcon_append_ctx_t *ctx) * defined up top. * */ void -bcon_append_ctx_va (bson_t *bson, bcon_append_ctx_t *ctx, va_list *ap) +bcon_append_ctx_va(bson_t *bson, bcon_append_ctx_t *ctx, va_list *ap) { bcon_type_t type; const char *key; @@ -747,53 +747,53 @@ bcon_append_ctx_va (bson_t *bson, bcon_append_ctx_t *ctx, va_list *ap) while (1) { if (STACK_IS_ARRAY) { - bson_uint32_to_string (STACK_I, &key, i_str, sizeof i_str); + bson_uint32_to_string(STACK_I, &key, i_str, sizeof i_str); STACK_I++; } else { - type = _bcon_append_tokenize (ap, &u); + type = _bcon_append_tokenize(ap, &u); if (type == BCON_TYPE_END) { return; } if (type == BCON_TYPE_DOC_END) { - STACK_POP_DOC (bson_append_document_end (STACK_BSON_PARENT, STACK_BSON_CHILD)); + STACK_POP_DOC(bson_append_document_end(STACK_BSON_PARENT, STACK_BSON_CHILD)); continue; } if (type == BCON_TYPE_BCON) { - bson_concat (STACK_BSON_CHILD, u.BCON); + bson_concat(STACK_BSON_CHILD, u.BCON); continue; } - BSON_ASSERT (type == BCON_TYPE_UTF8); + BSON_ASSERT(type == BCON_TYPE_UTF8); key = u.UTF8; } - type = _bcon_append_tokenize (ap, &u); - BSON_ASSERT (type != BCON_TYPE_END); + type = _bcon_append_tokenize(ap, &u); + BSON_ASSERT(type != BCON_TYPE_END); - switch ((int) type) { + switch ((int)type) { case BCON_TYPE_BCON: - BSON_ASSERT (STACK_IS_ARRAY); - _bson_concat_array (STACK_BSON_CHILD, u.BCON, ctx); + BSON_ASSERT(STACK_IS_ARRAY); + _bson_concat_array(STACK_BSON_CHILD, u.BCON, ctx); break; case BCON_TYPE_DOC_START: - STACK_PUSH_DOC (bson_append_document_begin (STACK_BSON_PARENT, key, -1, STACK_BSON_CHILD)); + STACK_PUSH_DOC(bson_append_document_begin(STACK_BSON_PARENT, key, -1, STACK_BSON_CHILD)); break; case BCON_TYPE_DOC_END: - STACK_POP_DOC (bson_append_document_end (STACK_BSON_PARENT, STACK_BSON_CHILD)); + STACK_POP_DOC(bson_append_document_end(STACK_BSON_PARENT, STACK_BSON_CHILD)); break; case BCON_TYPE_ARRAY_START: - STACK_PUSH_ARRAY (bson_append_array_begin (STACK_BSON_PARENT, key, -1, STACK_BSON_CHILD)); + STACK_PUSH_ARRAY(bson_append_array_unsafe_begin(STACK_BSON_PARENT, key, -1, STACK_BSON_CHILD)); break; case BCON_TYPE_ARRAY_END: - STACK_POP_ARRAY (bson_append_array_end (STACK_BSON_PARENT, STACK_BSON_CHILD)); + STACK_POP_ARRAY(bson_append_array_end(STACK_BSON_PARENT, STACK_BSON_CHILD)); break; default: - _bcon_append_single (STACK_BSON_CHILD, type, key, &u); + _bcon_append_single(STACK_BSON_CHILD, type, key, &u); break; } @@ -823,7 +823,7 @@ bcon_append_ctx_va (bson_t *bson, bcon_append_ctx_t *ctx, va_list *ap) * otherwise. * */ bool -bcon_extract_ctx_va (bson_t *bson, bcon_extract_ctx_t *ctx, va_list *ap) +bcon_extract_ctx_va(bson_t *bson, bcon_extract_ctx_t *ctx, va_list *ap) { bcon_type_t type; const char *key; @@ -833,63 +833,63 @@ bcon_extract_ctx_va (bson_t *bson, bcon_extract_ctx_t *ctx, va_list *ap) bcon_extract_t u = {0}; - BSON_ASSERT (bson_iter_init (&root_iter, bson)); + BSON_ASSERT(bson_iter_init(&root_iter, bson)); while (1) { if (STACK_IS_ARRAY) { - bson_uint32_to_string (STACK_I, &key, i_str, sizeof i_str); + bson_uint32_to_string(STACK_I, &key, i_str, sizeof i_str); STACK_I++; } else { - type = _bcon_extract_tokenize (ap, &u); + type = _bcon_extract_tokenize(ap, &u); if (type == BCON_TYPE_END) { return true; } if (type == BCON_TYPE_DOC_END) { - STACK_POP_DOC (_noop ()); + STACK_POP_DOC(_noop()); continue; } - BSON_ASSERT (type == BCON_TYPE_RAW); + BSON_ASSERT(type == BCON_TYPE_RAW); key = u.key; } - type = _bcon_extract_tokenize (ap, &u); - BSON_ASSERT (type != BCON_TYPE_END); + type = _bcon_extract_tokenize(ap, &u); + BSON_ASSERT(type != BCON_TYPE_END); if (type == BCON_TYPE_DOC_END) { - STACK_POP_DOC (_noop ()); + STACK_POP_DOC(_noop()); } else if (type == BCON_TYPE_ARRAY_END) { - STACK_POP_ARRAY (_noop ()); + STACK_POP_ARRAY(_noop()); } else { - memcpy (¤t_iter, STACK_ITER_CHILD, sizeof current_iter); + memcpy(¤t_iter, STACK_ITER_CHILD, sizeof current_iter); - if (!bson_iter_find (¤t_iter, key)) { + if (!bson_iter_find(¤t_iter, key)) { return false; } - switch ((int) type) { + switch ((int)type) { case BCON_TYPE_DOC_START: - if (bson_iter_type (¤t_iter) != BSON_TYPE_DOCUMENT) { + if (bson_iter_type(¤t_iter) != BSON_TYPE_DOCUMENT) { return false; } - STACK_PUSH_DOC (bson_iter_recurse (¤t_iter, STACK_ITER_CHILD)); + STACK_PUSH_DOC(bson_iter_recurse(¤t_iter, STACK_ITER_CHILD)); break; case BCON_TYPE_ARRAY_START: - if (bson_iter_type (¤t_iter) != BSON_TYPE_ARRAY) { + if (bson_iter_type(¤t_iter) != BSON_TYPE_ARRAY) { return false; } - STACK_PUSH_ARRAY (bson_iter_recurse (¤t_iter, STACK_ITER_CHILD)); + STACK_PUSH_ARRAY(bson_iter_recurse(¤t_iter, STACK_ITER_CHILD)); break; default: - if (!_bcon_extract_single (¤t_iter, type, &u)) { + if (!_bcon_extract_single(¤t_iter, type, &u)) { return false; } @@ -900,74 +900,74 @@ bcon_extract_ctx_va (bson_t *bson, bcon_extract_ctx_t *ctx, va_list *ap) } void -bcon_extract_ctx_init (bcon_extract_ctx_t *ctx) +bcon_extract_ctx_init(bcon_extract_ctx_t *ctx) { ctx->n = 0; ctx->stack[0].is_array = false; } bool -bcon_extract (bson_t *bson, ...) +bcon_extract(bson_t *bson, ...) { va_list ap; bcon_extract_ctx_t ctx; bool r; - bcon_extract_ctx_init (&ctx); + bcon_extract_ctx_init(&ctx); - va_start (ap, bson); + va_start(ap, bson); - r = bcon_extract_ctx_va (bson, &ctx, &ap); + r = bcon_extract_ctx_va(bson, &ctx, &ap); - va_end (ap); + va_end(ap); return r; } void -bcon_append (bson_t *bson, ...) +bcon_append(bson_t *bson, ...) { va_list ap; bcon_append_ctx_t ctx; - bcon_append_ctx_init (&ctx); + bcon_append_ctx_init(&ctx); - va_start (ap, bson); + va_start(ap, bson); - bcon_append_ctx_va (bson, &ctx, &ap); + bcon_append_ctx_va(bson, &ctx, &ap); - va_end (ap); + va_end(ap); } void -bcon_append_ctx (bson_t *bson, bcon_append_ctx_t *ctx, ...) +bcon_append_ctx(bson_t *bson, bcon_append_ctx_t *ctx, ...) { va_list ap; - va_start (ap, ctx); + va_start(ap, ctx); - bcon_append_ctx_va (bson, ctx, &ap); + bcon_append_ctx_va(bson, ctx, &ap); - va_end (ap); + va_end(ap); } void -bcon_extract_ctx (bson_t *bson, bcon_extract_ctx_t *ctx, ...) +bcon_extract_ctx(bson_t *bson, bcon_extract_ctx_t *ctx, ...) { va_list ap; - va_start (ap, ctx); + va_start(ap, ctx); - bcon_extract_ctx_va (bson, ctx, &ap); + bcon_extract_ctx_va(bson, ctx, &ap); - va_end (ap); + va_end(ap); } void -bcon_append_ctx_init (bcon_append_ctx_t *ctx) +bcon_append_ctx_init(bcon_append_ctx_t *ctx) { ctx->n = 0; ctx->stack[0].is_array = 0; @@ -975,21 +975,21 @@ bcon_append_ctx_init (bcon_append_ctx_t *ctx) bson_t * -bcon_new (void *unused, ...) +bcon_new(void *unused, ...) { va_list ap; bcon_append_ctx_t ctx; bson_t *bson; - bcon_append_ctx_init (&ctx); + bcon_append_ctx_init(&ctx); - bson = bson_new (); + bson = bson_new(); - va_start (ap, unused); + va_start(ap, unused); - bcon_append_ctx_va (bson, &ctx, &ap); + bcon_append_ctx_va(bson, &ctx, &ap); - va_end (ap); + va_end(ap); return bson; } diff --git a/bsonjs/bson/bson-bcon.h b/bsonjs/bson/bson-bcon.h new file mode 100644 index 0000000..7adcc40 --- /dev/null +++ b/bsonjs/bson/bson-bcon.h @@ -0,0 +1,249 @@ +/* + * @file bcon.h + * @brief BCON (BSON C Object Notation) Declarations + */ + +#include + +/* Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BCON_H_ +#define BCON_H_ + +// Include specific headers first, because bson.h tries to include this header itself: +#include +#include +// For other APIs, not properly grouped, but needed: +#include + + +BSON_BEGIN_DECLS + + +#define BCON_STACK_MAX 100 + +#define BCON_ENSURE_DECLARE(fun, type) \ + static BSON_INLINE type bcon_ensure_##fun(type _t) \ + { \ + return _t; \ + } + +#define BCON_ENSURE(fun, val) bcon_ensure_##fun(val) + +#define BCON_ENSURE_STORAGE(fun, val) bcon_ensure_##fun(&(val)) + +BCON_ENSURE_DECLARE(const_char_ptr, const char *) +BCON_ENSURE_DECLARE(const_char_ptr_ptr, const char **) +BCON_ENSURE_DECLARE(double, double) +BCON_ENSURE_DECLARE(double_ptr, double *) +BCON_ENSURE_DECLARE(const_bson_ptr, const bson_t *) +BCON_ENSURE_DECLARE(bson_ptr, bson_t *) +BCON_ENSURE_DECLARE(subtype, bson_subtype_t) +BCON_ENSURE_DECLARE(subtype_ptr, bson_subtype_t *) +BCON_ENSURE_DECLARE(const_uint8_ptr, const uint8_t *) +BCON_ENSURE_DECLARE(const_uint8_ptr_ptr, const uint8_t **) +BCON_ENSURE_DECLARE(uint32, uint32_t) +BCON_ENSURE_DECLARE(uint32_ptr, uint32_t *) +BCON_ENSURE_DECLARE(const_oid_ptr, const bson_oid_t *) +BCON_ENSURE_DECLARE(const_oid_ptr_ptr, const bson_oid_t **) +BCON_ENSURE_DECLARE(int32, int32_t) +BCON_ENSURE_DECLARE(int32_ptr, int32_t *) +BCON_ENSURE_DECLARE(int64, int64_t) +BCON_ENSURE_DECLARE(int64_ptr, int64_t *) +BCON_ENSURE_DECLARE(const_decimal128_ptr, const bson_decimal128_t *) +BCON_ENSURE_DECLARE(bool, bool) +BCON_ENSURE_DECLARE(bool_ptr, bool *) +BCON_ENSURE_DECLARE(bson_type, bson_type_t) +BCON_ENSURE_DECLARE(bson_iter_ptr, bson_iter_t *) +BCON_ENSURE_DECLARE(const_bson_iter_ptr, const bson_iter_t *) + +#define BCON_UTF8(_val) BCON_MAGIC, BCON_TYPE_UTF8, BCON_ENSURE(const_char_ptr, (_val)) +#define BCON_DOUBLE(_val) BCON_MAGIC, BCON_TYPE_DOUBLE, BCON_ENSURE(double, (_val)) +#define BCON_DOCUMENT(_val) BCON_MAGIC, BCON_TYPE_DOCUMENT, BCON_ENSURE(const_bson_ptr, (_val)) +#define BCON_ARRAY(_val) BCON_MAGIC, BCON_TYPE_ARRAY, BCON_ENSURE(const_bson_ptr, (_val)) +#define BCON_BIN(_subtype, _binary, _length) \ + BCON_MAGIC, BCON_TYPE_BIN, BCON_ENSURE(subtype, (_subtype)), BCON_ENSURE(const_uint8_ptr, (_binary)), \ + BCON_ENSURE(uint32, (_length)) +#define BCON_UNDEFINED BCON_MAGIC, BCON_TYPE_UNDEFINED +#define BCON_OID(_val) BCON_MAGIC, BCON_TYPE_OID, BCON_ENSURE(const_oid_ptr, (_val)) +#define BCON_BOOL(_val) BCON_MAGIC, BCON_TYPE_BOOL, BCON_ENSURE(bool, (_val)) +#define BCON_DATE_TIME(_val) BCON_MAGIC, BCON_TYPE_DATE_TIME, BCON_ENSURE(int64, (_val)) +#define BCON_NULL BCON_MAGIC, BCON_TYPE_NULL +#define BCON_REGEX(_regex, _flags) \ + BCON_MAGIC, BCON_TYPE_REGEX, BCON_ENSURE(const_char_ptr, (_regex)), BCON_ENSURE(const_char_ptr, (_flags)) +#define BCON_DBPOINTER(_collection, _oid) \ + BCON_MAGIC, BCON_TYPE_DBPOINTER, BCON_ENSURE(const_char_ptr, (_collection)), BCON_ENSURE(const_oid_ptr, (_oid)) +#define BCON_CODE(_val) BCON_MAGIC, BCON_TYPE_CODE, BCON_ENSURE(const_char_ptr, (_val)) +#define BCON_SYMBOL(_val) BCON_MAGIC, BCON_TYPE_SYMBOL, BCON_ENSURE(const_char_ptr, (_val)) +#define BCON_CODEWSCOPE(_js, _scope) \ + BCON_MAGIC, BCON_TYPE_CODEWSCOPE, BCON_ENSURE(const_char_ptr, (_js)), BCON_ENSURE(const_bson_ptr, (_scope)) +#define BCON_INT32(_val) BCON_MAGIC, BCON_TYPE_INT32, BCON_ENSURE(int32, (_val)) +#define BCON_TIMESTAMP(_timestamp, _increment) \ + BCON_MAGIC, BCON_TYPE_TIMESTAMP, BCON_ENSURE(int32, (_timestamp)), BCON_ENSURE(int32, (_increment)) +#define BCON_INT64(_val) BCON_MAGIC, BCON_TYPE_INT64, BCON_ENSURE(int64, (_val)) +#define BCON_DECIMAL128(_val) BCON_MAGIC, BCON_TYPE_DECIMAL128, BCON_ENSURE(const_decimal128_ptr, (_val)) +#define BCON_MAXKEY BCON_MAGIC, BCON_TYPE_MAXKEY +#define BCON_MINKEY BCON_MAGIC, BCON_TYPE_MINKEY +#define BCON(_val) BCON_MAGIC, BCON_TYPE_BCON, BCON_ENSURE(const_bson_ptr, (_val)) +#define BCON_ITER(_val) BCON_MAGIC, BCON_TYPE_ITER, BCON_ENSURE(const_bson_iter_ptr, (_val)) + +#define BCONE_UTF8(_val) BCONE_MAGIC, BCON_TYPE_UTF8, BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_val)) +#define BCONE_DOUBLE(_val) BCONE_MAGIC, BCON_TYPE_DOUBLE, BCON_ENSURE_STORAGE(double_ptr, (_val)) +#define BCONE_DOCUMENT(_val) BCONE_MAGIC, BCON_TYPE_DOCUMENT, BCON_ENSURE_STORAGE(bson_ptr, (_val)) +#define BCONE_ARRAY(_val) BCONE_MAGIC, BCON_TYPE_ARRAY, BCON_ENSURE_STORAGE(bson_ptr, (_val)) +#define BCONE_BIN(subtype, binary, length) \ + BCONE_MAGIC, BCON_TYPE_BIN, BCON_ENSURE_STORAGE(subtype_ptr, (subtype)), \ + BCON_ENSURE_STORAGE(const_uint8_ptr_ptr, (binary)), BCON_ENSURE_STORAGE(uint32_ptr, (length)) +#define BCONE_UNDEFINED BCONE_MAGIC, BCON_TYPE_UNDEFINED +#define BCONE_OID(_val) BCONE_MAGIC, BCON_TYPE_OID, BCON_ENSURE_STORAGE(const_oid_ptr_ptr, (_val)) +#define BCONE_BOOL(_val) BCONE_MAGIC, BCON_TYPE_BOOL, BCON_ENSURE_STORAGE(bool_ptr, (_val)) +#define BCONE_DATE_TIME(_val) BCONE_MAGIC, BCON_TYPE_DATE_TIME, BCON_ENSURE_STORAGE(int64_ptr, (_val)) +#define BCONE_NULL BCONE_MAGIC, BCON_TYPE_NULL +#define BCONE_REGEX(_regex, _flags) \ + BCONE_MAGIC, BCON_TYPE_REGEX, BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_regex)), \ + BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_flags)) +#define BCONE_DBPOINTER(_collection, _oid) \ + BCONE_MAGIC, BCON_TYPE_DBPOINTER, BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_collection)), \ + BCON_ENSURE_STORAGE(const_oid_ptr_ptr, (_oid)) +#define BCONE_CODE(_val) BCONE_MAGIC, BCON_TYPE_CODE, BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_val)) +#define BCONE_SYMBOL(_val) BCONE_MAGIC, BCON_TYPE_SYMBOL, BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_val)) +#define BCONE_CODEWSCOPE(_js, _scope) \ + BCONE_MAGIC, BCON_TYPE_CODEWSCOPE, BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_js)), \ + BCON_ENSURE_STORAGE(bson_ptr, (_scope)) +#define BCONE_INT32(_val) BCONE_MAGIC, BCON_TYPE_INT32, BCON_ENSURE_STORAGE(int32_ptr, (_val)) +#define BCONE_TIMESTAMP(_timestamp, _increment) \ + BCONE_MAGIC, BCON_TYPE_TIMESTAMP, BCON_ENSURE_STORAGE(int32_ptr, (_timestamp)), \ + BCON_ENSURE_STORAGE(int32_ptr, (_increment)) +#define BCONE_INT64(_val) BCONE_MAGIC, BCON_TYPE_INT64, BCON_ENSURE_STORAGE(int64_ptr, (_val)) +#define BCONE_DECIMAL128(_val) BCONE_MAGIC, BCON_TYPE_DECIMAL128, BCON_ENSURE_STORAGE(const_decimal128_ptr, (_val)) +#define BCONE_MAXKEY BCONE_MAGIC, BCON_TYPE_MAXKEY +#define BCONE_MINKEY BCONE_MAGIC, BCON_TYPE_MINKEY +#define BCONE_SKIP(_val) BCONE_MAGIC, BCON_TYPE_SKIP, BCON_ENSURE(bson_type, (_val)) +#define BCONE_ITER(_val) BCONE_MAGIC, BCON_TYPE_ITER, BCON_ENSURE_STORAGE(bson_iter_ptr, (_val)) + +#define BCON_MAGIC bson_bcon_magic() +#define BCONE_MAGIC bson_bcone_magic() + +typedef enum { + BCON_TYPE_UTF8, + BCON_TYPE_DOUBLE, + BCON_TYPE_DOCUMENT, + BCON_TYPE_ARRAY, + BCON_TYPE_BIN, + BCON_TYPE_UNDEFINED, + BCON_TYPE_OID, + BCON_TYPE_BOOL, + BCON_TYPE_DATE_TIME, + BCON_TYPE_NULL, + BCON_TYPE_REGEX, + BCON_TYPE_DBPOINTER, + BCON_TYPE_CODE, + BCON_TYPE_SYMBOL, + BCON_TYPE_CODEWSCOPE, + BCON_TYPE_INT32, + BCON_TYPE_TIMESTAMP, + BCON_TYPE_INT64, + BCON_TYPE_DECIMAL128, + BCON_TYPE_MAXKEY, + BCON_TYPE_MINKEY, + BCON_TYPE_BCON, + BCON_TYPE_ARRAY_START, + BCON_TYPE_ARRAY_END, + BCON_TYPE_DOC_START, + BCON_TYPE_DOC_END, + BCON_TYPE_END, + BCON_TYPE_RAW, + BCON_TYPE_SKIP, + BCON_TYPE_ITER, + BCON_TYPE_ERROR, +} bcon_type_t; + +typedef struct bcon_append_ctx_frame { + int i; + bool is_array; + bson_t bson; +} bcon_append_ctx_frame_t; + +typedef struct bcon_extract_ctx_frame { + int i; + bool is_array; + bson_iter_t iter; +} bcon_extract_ctx_frame_t; + +typedef struct _bcon_append_ctx_t { + bcon_append_ctx_frame_t stack[BCON_STACK_MAX]; + int n; +} bcon_append_ctx_t; + +typedef struct _bcon_extract_ctx_t { + bcon_extract_ctx_frame_t stack[BCON_STACK_MAX]; + int n; +} bcon_extract_ctx_t; + +BSON_EXPORT(void) +bcon_append(bson_t *bson, ...) BSON_GNUC_NULL_TERMINATED; +BSON_EXPORT(void) +bcon_append_ctx(bson_t *bson, bcon_append_ctx_t *ctx, ...) BSON_GNUC_NULL_TERMINATED; +BSON_EXPORT(void) +bcon_append_ctx_va(bson_t *bson, bcon_append_ctx_t *ctx, va_list *va); +BSON_EXPORT(void) +bcon_append_ctx_init(bcon_append_ctx_t *ctx); + +BSON_EXPORT(void) +bcon_extract_ctx_init(bcon_extract_ctx_t *ctx); + +BSON_EXPORT(void) +bcon_extract_ctx(bson_t *bson, bcon_extract_ctx_t *ctx, ...) BSON_GNUC_NULL_TERMINATED; + +BSON_EXPORT(bool) +bcon_extract_ctx_va(bson_t *bson, bcon_extract_ctx_t *ctx, va_list *ap); + +BSON_EXPORT(bool) +bcon_extract(bson_t *bson, ...) BSON_GNUC_NULL_TERMINATED; + +BSON_EXPORT(bool) +bcon_extract_va(bson_t *bson, bcon_extract_ctx_t *ctx, ...) BSON_GNUC_NULL_TERMINATED; + +BSON_EXPORT(bson_t *) +bcon_new(void *unused, ...) BSON_GNUC_NULL_TERMINATED; + +/** + * The bcon_..() functions are all declared with __attribute__((sentinel)). + * + * From GCC manual for "sentinel": "A valid NULL in this context is defined as + * zero with any pointer type. If your system defines the NULL macro with an + * integer type then you need to add an explicit cast." + * Case in point: GCC on Solaris (at least) + */ +#define BCON_APPEND(_bson, ...) bcon_append((_bson), __VA_ARGS__, (void *)NULL) +#define BCON_APPEND_CTX(_bson, _ctx, ...) bcon_append_ctx((_bson), (_ctx), __VA_ARGS__, (void *)NULL) + +#define BCON_EXTRACT(_bson, ...) bcon_extract((_bson), __VA_ARGS__, (void *)NULL) + +#define BCON_EXTRACT_CTX(_bson, _ctx, ...) bcon_extract((_bson), (_ctx), __VA_ARGS__, (void *)NULL) + +#define BCON_NEW(...) bcon_new(NULL, __VA_ARGS__, (void *)NULL) + +BSON_EXPORT(const char *) +bson_bcon_magic(void) BSON_GNUC_PURE; +BSON_EXPORT(const char *) +bson_bcone_magic(void) BSON_GNUC_PURE; + + +BSON_END_DECLS + + +#endif diff --git a/bsonjs/bson/bson-clock.c b/bsonjs/bson/bson-clock.c index a9510c2..83f66bb 100644 --- a/bsonjs/bson/bson-clock.c +++ b/bsonjs/bson/bson-clock.c @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,17 @@ * limitations under the License. */ -#include -#include +#include +#include + +#include +#include #if defined(BSON_HAVE_CLOCK_GETTIME) -#include #include + +#include #endif #include @@ -42,7 +46,7 @@ */ int -bson_gettimeofday (struct timeval *tv) /* OUT */ +bson_gettimeofday(struct timeval *tv) /* OUT */ { #if defined(_WIN32) #if defined(_MSC_VER) @@ -64,7 +68,7 @@ bson_gettimeofday (struct timeval *tv) /* OUT */ */ if (tv) { - GetSystemTimeAsFileTime (&ft); + GetSystemTimeAsFileTime(&ft); /* pull out of the filetime into a 64 bit uint */ tmp |= ft.dwHighDateTime; @@ -77,13 +81,13 @@ bson_gettimeofday (struct timeval *tv) /* OUT */ /* adjust to unix epoch */ tmp -= DELTA_EPOCH_IN_MICROSEC; - tv->tv_sec = (long) (tmp / 1000000UL); - tv->tv_usec = (long) (tmp % 1000000UL); + tv->tv_sec = (long)(tmp / 1000000UL); + tv->tv_usec = (long)(tmp % 1000000UL); } return 0; #else - return gettimeofday (tv, NULL); + return gettimeofday(tv, NULL); #endif } @@ -107,26 +111,8 @@ bson_gettimeofday (struct timeval *tv) /* OUT */ */ int64_t -bson_get_monotonic_time (void) +bson_get_monotonic_time(void) { -#if defined(BSON_HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) - struct timespec ts; - /* ts.tv_sec may be a four-byte integer on 32 bit machines, so cast to - * int64_t to avoid truncation. */ - clock_gettime (CLOCK_MONOTONIC, &ts); - return (((int64_t) ts.tv_sec * 1000000) + (ts.tv_nsec / 1000)); -#elif defined(_WIN32) - /* Despite it's name, this is in milliseconds! */ - int64_t ticks = GetTickCount64 (); - return (ticks * 1000); -#elif defined(__hpux__) - int64_t nanosec = gethrtime (); - return (nanosec / 1000UL); -#else -#pragma message "Monotonic clock is not yet supported on your platform." - struct timeval tv; - - bson_gettimeofday (&tv); - return ((int64_t) tv.tv_sec * 1000000) + tv.tv_usec; -#endif + mlib_time_point now = mlib_now(); + return mlib_microseconds_count(now.time_since_monotonic_start); } diff --git a/bsonjs/bson/bson-clock.h b/bsonjs/bson/bson-clock.h index a4845b7..5f7d8df 100644 --- a/bsonjs/bson/bson-clock.h +++ b/bsonjs/bson/bson-clock.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,18 +21,18 @@ #define BSON_CLOCK_H -#include -#include #include +#include +#include BSON_BEGIN_DECLS -BSON_EXPORT (int64_t) -bson_get_monotonic_time (void); -BSON_EXPORT (int) -bson_gettimeofday (struct timeval *tv); +BSON_EXPORT(int64_t) +bson_get_monotonic_time(void); +BSON_EXPORT(int) +bson_gettimeofday(struct timeval *tv); BSON_END_DECLS diff --git a/bsonjs/bson/bson-cmp.h b/bsonjs/bson/bson-cmp.h deleted file mode 100644 index 5a90d0f..0000000 --- a/bsonjs/bson/bson-cmp.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * 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 - - -#ifndef BSON_CMP_H -#define BSON_CMP_H - - -#include /* ssize_t */ -#include /* 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/bsonjs/bson/bson-context-private.h b/bsonjs/bson/bson-context-private.h index 0434973..8097df4 100644 --- a/bsonjs/bson/bson-context-private.h +++ b/bsonjs/bson/bson-context-private.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,9 +20,11 @@ #ifndef BSON_CONTEXT_PRIVATE_H #define BSON_CONTEXT_PRIVATE_H +#include // IWYU pragma: export -#include -#include "common-thread-private.h" +// + +#include BSON_BEGIN_DECLS @@ -53,7 +55,7 @@ struct _bson_context_t { * @param oid The OID to update. */ void -_bson_context_set_oid_rand (bson_context_t *context, bson_oid_t *oid); +_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 @@ -63,20 +65,7 @@ _bson_context_set_oid_rand (bson_context_t *context, bson_oid_t *oid); * @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_context_set_oid_seq32(bson_context_t *context, bson_oid_t *oid); BSON_END_DECLS diff --git a/bsonjs/bson/bson-context.c b/bsonjs/bson/bson-context.c index f9f545f..431468d 100644 --- a/bsonjs/bson/bson-context.c +++ b/bsonjs/bson/bson-context.c @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,18 @@ * limitations under the License. */ -#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include #include #include @@ -22,13 +33,6 @@ #include #include -#include -#include -#include -#include -#include -#include "common-thread-private.h" - #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 256 @@ -41,16 +45,16 @@ static bson_context_t gContextDefault; static BSON_INLINE uint64_t -_bson_getpid (void) +_bson_getpid(void) { uint64_t pid; #ifdef BSON_OS_WIN32 DWORD real_pid; - real_pid = GetCurrentProcessId (); + real_pid = GetCurrentProcessId(); pid = (real_pid & 0xFFFF) ^ ((real_pid >> 16) & 0xFFFF); #else - pid = (uint64_t) getpid (); + pid = (uint64_t)getpid(); #endif return pid; @@ -58,24 +62,13 @@ _bson_getpid (void) void -_bson_context_set_oid_seq32 (bson_context_t *context, /* IN */ - bson_oid_t *oid) /* OUT */ +_bson_context_set_oid_seq32(bson_context_t *context, /* IN */ + bson_oid_t *oid) /* OUT */ { - 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[BSON_OID_SEQ32_OFFSET], ((uint8_t *) &seq) + 1, BSON_OID_SEQ32_SIZE); -} - - -void -_bson_context_set_oid_seq64 (bson_context_t *context, /* IN */ - bson_oid_t *oid) /* OUT */ -{ - 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[BSON_OID_SEQ64_OFFSET], &seq, BSON_OID_SEQ64_SIZE); + uint32_t seq = (uint32_t)mcommon_atomic_int32_fetch_add( + (DECL_ATOMIC_INTEGRAL_INT32 *)&context->seq32, 1, mcommon_memory_order_seq_cst); + seq = BSON_UINT32_TO_BE(seq); + memcpy(&oid->bytes[BSON_OID_SEQ32_OFFSET], ((uint8_t *)&seq) + 1, BSON_OID_SEQ32_SIZE); } /* @@ -89,13 +82,13 @@ _bson_context_set_oid_seq64 (bson_context_t *context, /* IN */ * -------------------------------------------------------------------------- */ static void -_bson_context_get_hostname (char out[HOST_NAME_MAX]) +_bson_context_get_hostname(char out[HOST_NAME_MAX]) { - if (gethostname (out, HOST_NAME_MAX) != 0) { + if (gethostname(out, HOST_NAME_MAX) != 0) { if (errno == ENAMETOOLONG) { - fprintf (stderr, "hostname exceeds %d characters, truncating.", HOST_NAME_MAX); + fprintf(stderr, "hostname exceeds %d characters, truncating.", HOST_NAME_MAX); } else { - fprintf (stderr, "unable to get hostname: %d", errno); + fprintf(stderr, "unable to get hostname: %d", errno); } } out[HOST_NAME_MAX - 1] = '\0'; @@ -110,69 +103,52 @@ _bson_context_get_hostname (char out[HOST_NAME_MAX]) /* in-place rotate a 64bit number */ void -_bson_rotl_u64 (uint64_t *p, int nbits) +_bson_rotl_u64(uint64_t *p, int nbits) { *p = (*p << nbits) | (*p >> (64 - nbits)); } -/* 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) +_sip_round(uint64_t *v0, uint64_t *v1, uint64_t *v2, uint64_t *v3) { *v0 += *v1; - _bson_rotl_u64 (v1, 13); + _bson_rotl_u64(v1, 13); *v1 ^= *v0; - _bson_rotl_u64 (v0, 32); + _bson_rotl_u64(v0, 32); *v2 += *v3; - _bson_rotl_u64 (v3, 16); + _bson_rotl_u64(v3, 16); *v3 ^= *v2; *v0 += *v3; - _bson_rotl_u64 (v3, 21); + _bson_rotl_u64(v3, 21); *v3 ^= *v0; *v2 += *v1; - _bson_rotl_u64 (v1, 17); + _bson_rotl_u64(v1, 17); *v1 ^= *v2; - _bson_rotl_u64 (v2, 32); + _bson_rotl_u64(v2, 32); } void -_siphash (const void *in, const size_t inlen, const uint64_t key[2], uint64_t digest[2]) +_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; + 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 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 = mlib_read_u64le(kk); + uint64_t k1 = mlib_read_u64le(kk + 8); uint64_t m; int i; - const unsigned char *end = ni + inlen - (inlen % sizeof (uint64_t)); + const unsigned char *end = ni + inlen - (inlen % sizeof(uint64_t)); const int left = inlen & 7; - uint64_t b = ((uint64_t) inlen) << 56; + uint64_t b = ((uint64_t)inlen) << 56; v3 ^= k1; v2 ^= k0; v1 ^= k1; @@ -181,39 +157,39 @@ _siphash (const void *in, const size_t inlen, const uint64_t key[2], uint64_t di v1 ^= 0xee; for (; ni != end; ni += 8) { - m = _u8x8_le_to_u64 (ni); + m = mlib_read_u64le(ni); v3 ^= m; for (i = 0; i < C_ROUNDS; ++i) - _sip_round (&v0, &v1, &v2, &v3); + _sip_round(&v0, &v1, &v2, &v3); v0 ^= m; } switch (left) { case 7: - b |= ((uint64_t) ni[6]) << 48; + b |= ((uint64_t)ni[6]) << 48; /* FALLTHRU */ case 6: - b |= ((uint64_t) ni[5]) << 40; + b |= ((uint64_t)ni[5]) << 40; /* FALLTHRU */ case 5: - b |= ((uint64_t) ni[4]) << 32; + b |= ((uint64_t)ni[4]) << 32; /* FALLTHRU */ case 4: - b |= ((uint64_t) ni[3]) << 24; + b |= ((uint64_t)ni[3]) << 24; /* FALLTHRU */ case 3: - b |= ((uint64_t) ni[2]) << 16; + b |= ((uint64_t)ni[2]) << 16; /* FALLTHRU */ case 2: - b |= ((uint64_t) ni[1]) << 8; + b |= ((uint64_t)ni[1]) << 8; /* FALLTHRU */ case 1: - b |= ((uint64_t) ni[0]); + b |= ((uint64_t)ni[0]); break; default: - BSON_UNREACHABLE ("Invalid remainder during SipHash"); + BSON_UNREACHABLE("Invalid remainder during SipHash"); case 0: break; } @@ -221,27 +197,27 @@ _siphash (const void *in, const size_t inlen, const uint64_t key[2], uint64_t di v3 ^= b; for (i = 0; i < C_ROUNDS; ++i) - _sip_round (&v0, &v1, &v2, &v3); + _sip_round(&v0, &v1, &v2, &v3); v0 ^= b; v2 ^= 0xee; for (i = 0; i < D_ROUNDS; ++i) - _sip_round (&v0, &v1, &v2, &v3); + _sip_round(&v0, &v1, &v2, &v3); b = v0 ^ v1 ^ v2 ^ v3; - _u64_into_u8x8_le (digest_buf, b); + mlib_write_u64le(digest_buf, b); v1 ^= 0xdd; for (i = 0; i < D_ROUNDS; ++i) - _sip_round (&v0, &v1, &v2, &v3); + _sip_round(&v0, &v1, &v2, &v3); b = v0 ^ v1 ^ v2 ^ v3; - _u64_into_u8x8_le (digest_buf + 8, b); + mlib_write_u64le(digest_buf + 8, b); - memcpy (digest, digest_buf, sizeof digest_buf); + memcpy(digest, digest_buf, sizeof digest_buf); } /* @@ -259,7 +235,7 @@ struct _init_rand_params { }; static void -_bson_context_init_random (bson_context_t *context, bool init_seq) +_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 @@ -274,30 +250,31 @@ _bson_context_init_random (bson_context_t *context, bool init_seq) 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); + 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 = + mcommon_atomic_int64_fetch_add(&s_rand_call_counter, 1, mcommon_memory_order_seq_cst); /* Generate a SipHash key. We do not care about secrecy or determinism, only * uniqueness. */ - memcpy (key, &rand_params, sizeof key); + memcpy(key, &rand_params, sizeof key); key[1] = ~key[0]; /* Hash the param struct */ - _siphash (&rand_params, sizeof rand_params, key, digest); + _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); + 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); + 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); + context->seq32 &= ~UINT32_C(0xf0000f); + context->seq64 &= ~UINT64_C(0xf0000f); } /* Remember the PID we saw here. This may change in case of fork() */ @@ -305,64 +282,63 @@ _bson_context_init_random (bson_context_t *context, bool init_seq) } static void -_bson_context_init (bson_context_t *context, bson_context_flags_t flags) +_bson_context_init(bson_context_t *context, bson_context_flags_t flags) { - context->flags = (int) flags; - _bson_context_init_random (context, true /* Init counters */); + context->flags = (int)flags; + _bson_context_init_random(context, true /* Init counters */); } void -_bson_context_set_oid_rand (bson_context_t *context, bson_oid_t *oid) +_bson_context_set_oid_rand(bson_context_t *context, bson_oid_t *oid) { - BSON_ASSERT (context); - BSON_ASSERT (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 (); + uint64_t now_pid = _bson_getpid(); if (now_pid != context->pid) { - _bson_context_init_random (context, false /* Do not update the sequence counters */); + _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); + memcpy(oid->bytes + BSON_OID_RANDOMESS_OFFSET, &context->randomness, BSON_OID_RANDOMNESS_SIZE); } bson_context_t * -bson_context_new (bson_context_flags_t flags) +bson_context_new(bson_context_flags_t flags) { bson_context_t *context; - context = bson_malloc0 (sizeof *context); - _bson_context_init (context, flags); + context = bson_malloc0(sizeof *context); + _bson_context_init(context, flags); return context; } void -bson_context_destroy (bson_context_t *context) /* IN */ +bson_context_destroy(bson_context_t *context) /* IN */ { - bson_free (context); + bson_free(context); } - -static BSON_ONCE_FUN (_bson_context_init_default) +static BSON_ONCE_FUN(_bson_context_init_default) { - _bson_context_init (&gContextDefault, BSON_CONTEXT_DISABLE_PID_CACHE); + _bson_context_init(&gContextDefault, BSON_CONTEXT_DISABLE_PID_CACHE); BSON_ONCE_RETURN; } bson_context_t * -bson_context_get_default (void) +bson_context_get_default(void) { static bson_once_t once = BSON_ONCE_INIT; - bson_once (&once, _bson_context_init_default); + bson_once(&once, _bson_context_init_default); return &gContextDefault; } diff --git a/bsonjs/bson/bson-context.h b/bsonjs/bson/bson-context.h index 8399b57..26d03cb 100644 --- a/bsonjs/bson/bson-context.h +++ b/bsonjs/bson/bson-context.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,8 @@ #define BSON_CONTEXT_H -#include #include +#include BSON_BEGIN_DECLS @@ -40,22 +40,22 @@ BSON_BEGIN_DECLS * 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); +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); +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); +BSON_EXPORT(bson_context_t *) +bson_context_get_default(void); BSON_END_DECLS diff --git a/bsonjs/bson/bson-decimal128.c b/bsonjs/bson/bson-decimal128.c index 786963c..786b565 100644 --- a/bsonjs/bson/bson-decimal128.c +++ b/bsonjs/bson/bson-decimal128.c @@ -1,6 +1,5 @@ - /* - * Copyright 2015 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +14,19 @@ * limitations under the License. */ -#include -#include -#include - -#include #include + +#include + #include -#include -#include +#include + +#include +#include + +#include +#include +#include #define BSON_DECIMAL128_EXPONENT_MAX 6111 @@ -36,13 +39,13 @@ (dec).high = 0x7c00000000000000ull; \ (dec).low = 0; \ } else \ - (void) 0 + (void)0 #define BSON_DECIMAL128_SET_INF(dec, isneg) \ if (1) { \ (dec).high = 0x7800000000000000ull + 0x8000000000000000ull * (isneg); \ (dec).low = 0; \ } else \ - (void) 0 + (void)0 /** * _bson_uint128_t: @@ -77,9 +80,9 @@ typedef struct { *------------------------------------------------------------------------------ */ static void -_bson_uint128_divide1B (_bson_uint128_t value, /* IN */ - _bson_uint128_t *quotient, /* OUT */ - uint32_t *rem) /* OUT */ +_bson_uint128_divide1B(_bson_uint128_t value, /* IN */ + _bson_uint128_t *quotient, /* OUT */ + uint32_t *rem) /* OUT */ { const uint32_t DIVISOR = 1000 * 1000 * 1000; uint64_t _rem = 0; @@ -95,12 +98,12 @@ _bson_uint128_divide1B (_bson_uint128_t value, /* IN */ for (i = 0; i <= 3; i++) { _rem <<= 32; /* Adjust remainder to match value of next dividend */ _rem += value.parts[i]; /* Add the divided to _rem */ - value.parts[i] = (uint32_t) (_rem / DIVISOR); + value.parts[i] = (uint32_t)(_rem / DIVISOR); _rem %= DIVISOR; /* Store the remainder */ } *quotient = value; - *rem = (uint32_t) _rem; + *rem = (uint32_t)_rem; } @@ -125,19 +128,18 @@ _bson_uint128_divide1B (_bson_uint128_t value, /* IN */ *------------------------------------------------------------------------------ */ void -bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ - char *str) /* OUT */ +bson_decimal128_to_string(const bson_decimal128_t *dec, /* IN */ + char *str) /* OUT */ { - uint32_t COMBINATION_MASK = 0x1f; /* Extract least significant 5 bits */ - uint32_t EXPONENT_MASK = 0x3fff; /* Extract least significant 14 bits */ - uint32_t COMBINATION_INFINITY = 30; /* Value of combination field for Inf */ - uint32_t COMBINATION_NAN = 31; /* Value of combination field for NaN */ - uint32_t EXPONENT_BIAS = 6176; /* decimal128 exponent bias */ + const int COMBINATION_MASK = 0x1f; /* Extract least significant 5 bits */ + const int EXPONENT_MASK = 0x3fff; /* Extract least significant 14 bits */ + const int COMBINATION_INFINITY = 30; /* Value of combination field for Inf */ + const int COMBINATION_NAN = 31; /* Value of combination field for NaN */ + const int EXPONENT_BIAS = 6176; /* decimal128 exponent bias */ char *str_out = str; /* output pointer in string */ char significand_str[35]; /* decoded significand digits */ - /* Note: bits in this routine are referred to starting at 0, */ /* from the sign bit, towards the coefficient. */ uint32_t high; /* bits 0 - 31 */ @@ -145,40 +147,36 @@ bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ uint32_t midl; /* bits 64 - 95 */ uint32_t low; /* bits 96 - 127 */ uint32_t combination; /* bits 1 - 5 */ - uint32_t biased_exponent; /* decoded biased exponent (14 bits) */ - uint32_t significand_digits = 0; /* the number of significand digits */ uint32_t significand[36] = {0}; /* the base-10 digits in the significand */ uint32_t *significand_read = significand; /* read pointer into significand */ - int32_t exponent; /* unbiased exponent */ - int32_t scientific_exponent; /* the exponent if scientific notation is - * used */ bool is_zero = false; /* true if the number is zero */ uint8_t significand_msb; /* the most signifcant significand bits (50-46) */ _bson_uint128_t significand128; /* temporary storage for significand decoding */ - memset (significand_str, 0, sizeof (significand_str)); + memset(significand_str, 0, sizeof(significand_str)); - if ((int64_t) dec->high < 0) { /* negative */ + if ((int64_t)dec->high < 0) { /* negative */ *(str_out++) = '-'; } - low = (uint32_t) dec->low; - midl = (uint32_t) (dec->low >> 32); - midh = (uint32_t) dec->high; - high = (uint32_t) (dec->high >> 32); + low = (uint32_t)dec->low; + midl = (uint32_t)(dec->low >> 32); + midh = (uint32_t)dec->high; + high = (uint32_t)(dec->high >> 32); /* Decode combination field and exponent */ combination = (high >> 26) & COMBINATION_MASK; - if (BSON_UNLIKELY ((combination >> 3) == 3)) { + int biased_exponent; + if (BSON_UNLIKELY((combination >> 3) == 3)) { /* Check for 'special' values */ - if (combination == COMBINATION_INFINITY) { /* Infinity */ - strcpy (str_out, BSON_DECIMAL128_INF); + if (mlib_cmp(combination, ==, COMBINATION_INFINITY)) { /* Infinity */ + strcpy(str_out, BSON_DECIMAL128_INF); return; - } else if (combination == COMBINATION_NAN) { /* NaN */ + } else if (mlib_cmp(combination, ==, COMBINATION_NAN)) { /* NaN */ /* str, not str_out, to erase the sign */ - strcpy (str, BSON_DECIMAL128_NAN); + strcpy(str, BSON_DECIMAL128_NAN); /* we don't care about the NaN payload. */ return; } else { @@ -190,13 +188,15 @@ bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ biased_exponent = (high >> 17) & EXPONENT_MASK; } - exponent = biased_exponent - EXPONENT_BIAS; + /* unbiased exponent */ + const int32_t exponent = biased_exponent - EXPONENT_BIAS; + /* Create string of significand digits */ /* Convert the 114-bit binary number represented by */ /* (high, midh, midl, low) to at most 34 decimal */ /* digits through modulo and division. */ - significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xf) << 14); + significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xfu) << 14); significand128.parts[1] = midh; significand128.parts[2] = midl; significand128.parts[3] = low; @@ -215,7 +215,7 @@ bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ } else { for (int k = 3; k >= 0; k--) { uint32_t least_digits = 0; - _bson_uint128_divide1B (significand128, &significand128, &least_digits); + _bson_uint128_divide1B(significand128, &significand128, &least_digits); /* We now have the 9 least significant digits (in base 2). */ /* Convert and output to string. */ @@ -234,18 +234,20 @@ bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ /* Scientific - [-]d.dddE(+/-)dd or [-]dE(+/-)dd */ /* Regular - ddd.ddd */ + int32_t num_significant_digits; if (is_zero) { - significand_digits = 1; + num_significant_digits = 1; *significand_read = 0; } else { - significand_digits = 36; + num_significant_digits = 36; while (!(*significand_read)) { - significand_digits--; + num_significant_digits--; significand_read++; } } - scientific_exponent = significand_digits - 1 + exponent; + /* the exponent if scientific notation is used */ + const int32_t scientific_exponent = num_significant_digits - 1 + exponent; /* The scientific exponent checks are dictated by the string conversion * specification and are somewhat arbitrary cutoffs. @@ -258,30 +260,36 @@ bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ if (scientific_exponent < -6 || exponent > 0) { /* Scientific format */ *(str_out++) = *(significand_read++) + '0'; - significand_digits--; + num_significant_digits--; - if (significand_digits) { + if (num_significant_digits) { *(str_out++) = '.'; } - for (uint32_t i = 0; i < significand_digits && (str_out - str) < 36; i++) { + for (int32_t i = 0; i < num_significant_digits && (str_out - str) < 36; i++) { *(str_out++) = *(significand_read++) + '0'; } /* Exponent */ *(str_out++) = 'E'; - bson_snprintf (str_out, 6, "%+d", scientific_exponent); + // Truncation is OK. + int req = bson_snprintf(str_out, 6, "%+d", scientific_exponent); + BSON_ASSERT(req > 0); } else { /* Regular format with no decimal place */ if (exponent >= 0) { - for (uint32_t i = 0; i < significand_digits && (str_out - str) < 36; i++) { + for (int32_t i = 0; i < num_significant_digits && (str_out - str) < 36; i++) { *(str_out++) = *(significand_read++) + '0'; } *str_out = '\0'; } else { - int32_t radix_position = significand_digits + exponent; + int32_t radix_position = num_significant_digits + exponent; + + // Reserve space for null terminator. + const int available_bytes = BSON_DECIMAL128_STRING - 1; + const char *const str_end = str + available_bytes; if (radix_position > 0) { /* non-zero digits before radix */ - for (int32_t i = 0; i < radix_position && (str_out - str) < BSON_DECIMAL128_STRING; i++) { + for (int32_t i = 0; i < radix_position && str_out != str_end; i++) { *(str_out++) = *(significand_read++) + '0'; } } else { /* leading zero before radix point */ @@ -289,14 +297,16 @@ bson_decimal128_to_string (const bson_decimal128_t *dec, /* IN */ } *(str_out++) = '.'; - while (radix_position++ < 0) { /* add leading zeros after radix */ + while (radix_position++ < 0 && str_out != str_end) { /* add leading zeros after radix */ *(str_out++) = '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'; + const unsigned dot_pos = (unsigned)BSON_MAX(radix_position - 1, 0); + const unsigned n_trailing_digits = num_significant_digits - dot_pos; + const unsigned n_to_write = BSON_MIN(n_trailing_digits, (unsigned)(str_end - str_out)); + mlib_foreach_urange (i, n_to_write) { + (void)i; + *str_out++ = *significand_read++ + '0'; } *str_out = '\0'; } @@ -324,9 +334,9 @@ typedef struct { *------------------------------------------------------------------------- */ static void -_mul_64x64 (uint64_t left, /* IN */ - uint64_t right, /* IN */ - _bson_uint128_6464_t *product) /* OUT */ +_mul_64x64(uint64_t left, /* IN */ + uint64_t right, /* IN */ + _bson_uint128_6464_t *product) /* OUT */ { uint64_t left_high, left_low, right_high, right_low, product_high, product_mid, product_mid2, product_low; _bson_uint128_6464_t rt = {0}; @@ -337,9 +347,9 @@ _mul_64x64 (uint64_t left, /* IN */ } left_high = left >> 32; - left_low = (uint32_t) left; + left_low = (uint32_t)left; right_high = right >> 32; - right_low = (uint32_t) right; + right_low = (uint32_t)right; product_high = left_high * right_high; product_mid = left_high * right_low; @@ -347,10 +357,10 @@ _mul_64x64 (uint64_t left, /* IN */ product_low = left_low * right_low; product_high += product_mid >> 32; - product_mid = (uint32_t) product_mid + product_mid2 + (product_low >> 32); + product_mid = (uint32_t)product_mid + product_mid2 + (product_low >> 32); product_high = product_high + (product_mid >> 32); - product_low = (product_mid << 32) + (uint32_t) product_low; + product_low = (product_mid << 32) + (uint32_t)product_low; rt.high = product_high; rt.low = product_low; @@ -369,9 +379,9 @@ _mul_64x64 (uint64_t left, /* IN */ * The lowercased character. */ char -_dec128_tolower (char c) +_dec128_tolower(char c) { - if (isupper (c)) { + if (isupper(c)) { c += 32; } @@ -390,8 +400,8 @@ _dec128_tolower (char c) * true if the strings are equal, false otherwise. */ bool -_dec128_istreq (const char *a, /* IN */ - const char *b /* IN */) +_dec128_istreq(const char *a, /* IN */ + const char *b /* IN */) { while (*a != '\0' || *b != '\0') { /* strings are different lengths. */ @@ -399,7 +409,7 @@ _dec128_istreq (const char *a, /* IN */ return false; } - if (_dec128_tolower (*a) != _dec128_tolower (*b)) { + if (_dec128_tolower(*a) != _dec128_tolower(*b)) { return false; } @@ -435,10 +445,10 @@ _dec128_istreq (const char *a, /* IN */ *------------------------------------------------------------------------------ */ bool -bson_decimal128_from_string (const char *string, /* IN */ - bson_decimal128_t *dec) /* OUT */ +bson_decimal128_from_string(const char *string, /* IN */ + bson_decimal128_t *dec) /* OUT */ { - return bson_decimal128_from_string_w_len (string, -1, dec); + return bson_decimal128_from_string_w_len(string, -1, dec); } @@ -468,9 +478,9 @@ bson_decimal128_from_string (const char *string, /* IN */ *------------------------------------------------------------------------------ */ bool -bson_decimal128_from_string_w_len (const char *string, /* IN */ - int len, /* IN */ - bson_decimal128_t *dec) /* OUT */ +bson_decimal128_from_string_w_len(const char *string, /* IN */ + int len, /* IN */ + bson_decimal128_t *dec) /* OUT */ { _bson_uint128_6464_t significand = {0}; @@ -500,7 +510,7 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ uint64_t significand_low = 0; /* The low 17 digits of the significand */ uint16_t biased_exponent = 0; /* The biased exponent */ - BSON_ASSERT (dec); + BSON_ASSERT(dec); dec->high = 0; dec->low = 0; @@ -510,24 +520,24 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ } /* Check for Infinity or NaN */ - if (!isdigit (*str_read) && *str_read != '.') { - if (_dec128_istreq (str_read, "inf") || _dec128_istreq (str_read, "infinity")) { - BSON_DECIMAL128_SET_INF (*dec, is_negative); + if (!isdigit(*str_read) && *str_read != '.') { + if (_dec128_istreq(str_read, "inf") || _dec128_istreq(str_read, "infinity")) { + BSON_DECIMAL128_SET_INF(*dec, is_negative); return true; - } else if (_dec128_istreq (str_read, "nan")) { - BSON_DECIMAL128_SET_NAN (*dec); + } else if (_dec128_istreq(str_read, "nan")) { + BSON_DECIMAL128_SET_NAN(*dec); return true; } - BSON_DECIMAL128_SET_NAN (*dec); + BSON_DECIMAL128_SET_NAN(*dec); return false; } /* Read digits */ - while (((isdigit (*str_read) || *str_read == '.')) && (len == -1 || str_read < string + len)) { + while (((isdigit(*str_read) || *str_read == '.')) && (len == -1 || str_read < string + len)) { if (*str_read == '.') { if (saw_radix) { - BSON_DECIMAL128_SET_NAN (*dec); + BSON_DECIMAL128_SET_NAN(*dec); return false; } @@ -543,7 +553,7 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ } found_nonzero = true; - *(digits_insert++) = *(str_read) - '0'; /* Only store 34 digits */ + *(digits_insert++) = (uint16_t)(*(str_read) - '0'); /* Only store 34 digits */ ndigits_stored++; } } @@ -561,7 +571,7 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ } if (saw_radix && !ndigits_read) { - BSON_DECIMAL128_SET_NAN (*dec); + BSON_DECIMAL128_SET_NAN(*dec); return false; } @@ -574,20 +584,20 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ #define SSCANF sscanf #endif int64_t temp_exponent = 0; - int read_exponent = SSCANF (++str_read, "%" SCNd64 "%n", &temp_exponent, &nread); + int read_exponent = SSCANF(++str_read, "%" SCNd64 "%n", &temp_exponent, &nread); str_read += nread; - if (!read_exponent || nread == 0 || !bson_in_range_int32_t_signed (temp_exponent)) { - BSON_DECIMAL128_SET_NAN (*dec); + if (!read_exponent || nread == 0 || !mlib_in_range(int32_t, temp_exponent)) { + BSON_DECIMAL128_SET_NAN(*dec); return false; } - exponent = (int32_t) temp_exponent; + exponent = (int32_t)temp_exponent; #undef SSCANF } if ((len == -1 || str_read < string + len) && *str_read) { - BSON_DECIMAL128_SET_NAN (*dec); + BSON_DECIMAL128_SET_NAN(*dec); return false; } @@ -596,7 +606,6 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ first_digit = 0; if (!ndigits_stored) { /* value is zero */ - first_digit = 0; last_digit = 0; digits[0] = 0; ndigits = 1; @@ -618,12 +627,11 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ /* to represent user input */ /* Overflow prevention */ - if (bson_cmp_less_equal_su (exponent, radix_position) && - bson_cmp_greater_us (radix_position, exponent + (1 << 14))) { + if (mlib_cmp(exponent, <=, radix_position) && mlib_cmp(radix_position, >, exponent + (1 << 14))) { exponent = BSON_DECIMAL128_EXPONENT_MIN; } else { - BSON_ASSERT (bson_in_range_unsigned (int32_t, radix_position)); - exponent -= (int32_t) radix_position; + BSON_ASSERT(mlib_in_range(int32_t, radix_position)); + exponent -= (int32_t)radix_position; } /* Attempt to normalize the exponent */ @@ -640,7 +648,7 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ } /* Overflow is not permitted, error. */ - BSON_DECIMAL128_SET_NAN (*dec); + BSON_DECIMAL128_SET_NAN(*dec); return false; } @@ -656,13 +664,13 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ break; } - BSON_DECIMAL128_SET_NAN (*dec); + BSON_DECIMAL128_SET_NAN(*dec); return false; } if (ndigits_stored < ndigits) { if (string[ndigits - 1 + includes_sign + saw_radix] - '0' != 0 && significant_digits != 0) { - BSON_DECIMAL128_SET_NAN (*dec); + BSON_DECIMAL128_SET_NAN(*dec); return false; } @@ -670,7 +678,7 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ } else { if (digits[last_digit] != 0) { /* Inexact rounding is not allowed. */ - BSON_DECIMAL128_SET_NAN (*dec); + BSON_DECIMAL128_SET_NAN(*dec); return false; } @@ -681,7 +689,7 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ if (exponent < BSON_DECIMAL128_EXPONENT_MAX) { exponent++; } else { - BSON_DECIMAL128_SET_NAN (*dec); + BSON_DECIMAL128_SET_NAN(*dec); return false; } } @@ -689,15 +697,13 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ /* Round */ /* We've normalized the exponent, but might still need to round. */ if (last_digit - first_digit + 1 < significant_digits) { - uint8_t round_digit; - /* There are non-zero digits after last_digit that need rounding. */ /* We round to nearest, ties to even */ - round_digit = string[first_nonzero + last_digit + includes_sign + saw_radix + 1] - '0'; + const char round_digit = string[first_nonzero + last_digit + includes_sign + saw_radix + 1] - '0'; if (round_digit != 0) { /* Inexact (non-zero) rounding is not allowed */ - BSON_DECIMAL128_SET_NAN (*dec); + BSON_DECIMAL128_SET_NAN(*dec); return false; } } @@ -733,7 +739,7 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ } } - _mul_64x64 (significand_high, 100000000000000000ull, &significand); + _mul_64x64(significand_high, 100000000000000000ull, &significand); significand.low += significand_low; if (significand.low < significand_low) { @@ -741,7 +747,7 @@ bson_decimal128_from_string_w_len (const char *string, /* IN */ } - biased_exponent = (exponent + (int16_t) BSON_DECIMAL128_EXPONENT_BIAS); + biased_exponent = (exponent + (int16_t)BSON_DECIMAL128_EXPONENT_BIAS); /* Encode combination, exponent, and significand. */ if ((significand.high >> 49) & 1) { diff --git a/bsonjs/bson/bson-decimal128.h b/bsonjs/bson/bson-decimal128.h index aa1d53a..ddb9bb8 100644 --- a/bsonjs/bson/bson-decimal128.h +++ b/bsonjs/bson/bson-decimal128.h @@ -1,5 +1,5 @@ /* - * Copyright 2015 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,11 +21,11 @@ #define BSON_DECIMAL128_H -#include - -#include -#include #include +#include +#include + +#include /** @@ -45,16 +45,16 @@ BSON_BEGIN_DECLS -BSON_EXPORT (void) -bson_decimal128_to_string (const bson_decimal128_t *dec, char *str); +BSON_EXPORT(void) +bson_decimal128_to_string(const bson_decimal128_t *dec, char *str); /* Note: @string must be ASCII characters only! */ -BSON_EXPORT (bool) -bson_decimal128_from_string (const char *string, bson_decimal128_t *dec); +BSON_EXPORT(bool) +bson_decimal128_from_string(const char *string, bson_decimal128_t *dec); -BSON_EXPORT (bool) -bson_decimal128_from_string_w_len (const char *string, int len, bson_decimal128_t *dec); +BSON_EXPORT(bool) +bson_decimal128_from_string_w_len(const char *string, int len, bson_decimal128_t *dec); BSON_END_DECLS diff --git a/bsonjs/bson/bson-endian.h b/bsonjs/bson/bson-endian.h index 1527f10..2830cdf 100644 --- a/bsonjs/bson/bson-endian.h +++ b/bsonjs/bson/bson-endian.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,14 +20,9 @@ #ifndef BSON_ENDIAN_H #define BSON_ENDIAN_H - -#if defined(__sun) -#include -#endif - -#include -#include -#include +#include +#include +#include BSON_BEGIN_DECLS @@ -36,78 +31,77 @@ BSON_BEGIN_DECLS #define BSON_BIG_ENDIAN 4321 #define BSON_LITTLE_ENDIAN 1234 - -#if defined(__sun) -#define BSON_UINT16_SWAP_LE_BE(v) BSWAP_16 ((uint16_t) v) -#define BSON_UINT32_SWAP_LE_BE(v) BSWAP_32 ((uint32_t) v) -#define BSON_UINT64_SWAP_LE_BE(v) BSWAP_64 ((uint64_t) v) -#elif defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) && (__clang_major__ >= 3) && \ +#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) && (__clang_major__ >= 3) && \ (__clang_minor__ >= 1) #if __has_builtin(__builtin_bswap16) -#define BSON_UINT16_SWAP_LE_BE(v) __builtin_bswap16 (v) +#define BSON_UINT16_SWAP_LE_BE(v) __builtin_bswap16(v) #endif #if __has_builtin(__builtin_bswap32) -#define BSON_UINT32_SWAP_LE_BE(v) __builtin_bswap32 (v) +#define BSON_UINT32_SWAP_LE_BE(v) __builtin_bswap32(v) #endif #if __has_builtin(__builtin_bswap64) -#define BSON_UINT64_SWAP_LE_BE(v) __builtin_bswap64 (v) +#define BSON_UINT64_SWAP_LE_BE(v) __builtin_bswap64(v) #endif #elif defined(__GNUC__) && (__GNUC__ >= 4) #if __GNUC__ > 4 || (defined(__GNUC_MINOR__) && __GNUC_MINOR__ >= 3) -#define BSON_UINT32_SWAP_LE_BE(v) __builtin_bswap32 ((uint32_t) v) -#define BSON_UINT64_SWAP_LE_BE(v) __builtin_bswap64 ((uint64_t) v) +#define BSON_UINT32_SWAP_LE_BE(v) __builtin_bswap32((uint32_t)v) +#define BSON_UINT64_SWAP_LE_BE(v) __builtin_bswap64((uint64_t)v) #endif #if __GNUC__ > 4 || (defined(__GNUC_MINOR__) && __GNUC_MINOR__ >= 8) -#define BSON_UINT16_SWAP_LE_BE(v) __builtin_bswap16 ((uint32_t) v) +#define BSON_UINT16_SWAP_LE_BE(v) __builtin_bswap16((uint32_t)v) #endif #endif #ifndef BSON_UINT16_SWAP_LE_BE -#define BSON_UINT16_SWAP_LE_BE(v) __bson_uint16_swap_slow ((uint16_t) v) +#define BSON_UINT16_SWAP_LE_BE(v) __bson_uint16_swap_slow((uint16_t)v) #endif #ifndef BSON_UINT32_SWAP_LE_BE -#define BSON_UINT32_SWAP_LE_BE(v) __bson_uint32_swap_slow ((uint32_t) v) +#define BSON_UINT32_SWAP_LE_BE(v) __bson_uint32_swap_slow((uint32_t)v) #endif #ifndef BSON_UINT64_SWAP_LE_BE -#define BSON_UINT64_SWAP_LE_BE(v) __bson_uint64_swap_slow ((uint64_t) v) +#define BSON_UINT64_SWAP_LE_BE(v) __bson_uint64_swap_slow((uint64_t)v) #endif #if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN -#define BSON_UINT16_FROM_LE(v) ((uint16_t) v) -#define BSON_UINT16_TO_LE(v) ((uint16_t) v) -#define BSON_UINT16_FROM_BE(v) BSON_UINT16_SWAP_LE_BE (v) -#define BSON_UINT16_TO_BE(v) BSON_UINT16_SWAP_LE_BE (v) -#define BSON_UINT32_FROM_LE(v) ((uint32_t) v) -#define BSON_UINT32_TO_LE(v) ((uint32_t) v) -#define BSON_UINT32_FROM_BE(v) BSON_UINT32_SWAP_LE_BE (v) -#define BSON_UINT32_TO_BE(v) BSON_UINT32_SWAP_LE_BE (v) -#define BSON_UINT64_FROM_LE(v) ((uint64_t) v) -#define BSON_UINT64_TO_LE(v) ((uint64_t) v) -#define BSON_UINT64_FROM_BE(v) BSON_UINT64_SWAP_LE_BE (v) -#define BSON_UINT64_TO_BE(v) BSON_UINT64_SWAP_LE_BE (v) -#define BSON_DOUBLE_FROM_LE(v) ((double) v) -#define BSON_DOUBLE_TO_LE(v) ((double) v) +#define BSON_UINT16_FROM_LE(v) ((uint16_t)v) +#define BSON_UINT16_TO_LE(v) ((uint16_t)v) +#define BSON_UINT16_FROM_BE(v) BSON_UINT16_SWAP_LE_BE(v) +#define BSON_UINT16_TO_BE(v) BSON_UINT16_SWAP_LE_BE(v) +#define BSON_UINT32_FROM_LE(v) ((uint32_t)v) +#define BSON_UINT32_TO_LE(v) ((uint32_t)v) +#define BSON_UINT32_FROM_BE(v) BSON_UINT32_SWAP_LE_BE(v) +#define BSON_UINT32_TO_BE(v) BSON_UINT32_SWAP_LE_BE(v) +#define BSON_UINT64_FROM_LE(v) ((uint64_t)v) +#define BSON_UINT64_TO_LE(v) ((uint64_t)v) +#define BSON_UINT64_FROM_BE(v) BSON_UINT64_SWAP_LE_BE(v) +#define BSON_UINT64_TO_BE(v) BSON_UINT64_SWAP_LE_BE(v) +#define BSON_DOUBLE_FROM_LE(v) ((double)v) +#define BSON_DOUBLE_TO_LE(v) ((double)v) +#define BSON_FLOAT_FROM_LE(v) ((float)v) +#define BSON_FLOAT_TO_LE(v) ((float)v) #elif BSON_BYTE_ORDER == BSON_BIG_ENDIAN -#define BSON_UINT16_FROM_LE(v) BSON_UINT16_SWAP_LE_BE (v) -#define BSON_UINT16_TO_LE(v) BSON_UINT16_SWAP_LE_BE (v) -#define BSON_UINT16_FROM_BE(v) ((uint16_t) v) -#define BSON_UINT16_TO_BE(v) ((uint16_t) v) -#define BSON_UINT32_FROM_LE(v) BSON_UINT32_SWAP_LE_BE (v) -#define BSON_UINT32_TO_LE(v) BSON_UINT32_SWAP_LE_BE (v) -#define BSON_UINT32_FROM_BE(v) ((uint32_t) v) -#define BSON_UINT32_TO_BE(v) ((uint32_t) v) -#define BSON_UINT64_FROM_LE(v) BSON_UINT64_SWAP_LE_BE (v) -#define BSON_UINT64_TO_LE(v) BSON_UINT64_SWAP_LE_BE (v) -#define BSON_UINT64_FROM_BE(v) ((uint64_t) v) -#define BSON_UINT64_TO_BE(v) ((uint64_t) v) -#define BSON_DOUBLE_FROM_LE(v) (__bson_double_swap_slow (v)) -#define BSON_DOUBLE_TO_LE(v) (__bson_double_swap_slow (v)) +#define BSON_UINT16_FROM_LE(v) BSON_UINT16_SWAP_LE_BE(v) +#define BSON_UINT16_TO_LE(v) BSON_UINT16_SWAP_LE_BE(v) +#define BSON_UINT16_FROM_BE(v) ((uint16_t)v) +#define BSON_UINT16_TO_BE(v) ((uint16_t)v) +#define BSON_UINT32_FROM_LE(v) BSON_UINT32_SWAP_LE_BE(v) +#define BSON_UINT32_TO_LE(v) BSON_UINT32_SWAP_LE_BE(v) +#define BSON_UINT32_FROM_BE(v) ((uint32_t)v) +#define BSON_UINT32_TO_BE(v) ((uint32_t)v) +#define BSON_UINT64_FROM_LE(v) BSON_UINT64_SWAP_LE_BE(v) +#define BSON_UINT64_TO_LE(v) BSON_UINT64_SWAP_LE_BE(v) +#define BSON_UINT64_FROM_BE(v) ((uint64_t)v) +#define BSON_UINT64_TO_BE(v) ((uint64_t)v) +#define BSON_DOUBLE_FROM_LE(v) (__bson_double_swap_slow(v)) +#define BSON_DOUBLE_TO_LE(v) (__bson_double_swap_slow(v)) +#define BSON_FLOAT_FROM_LE(v) (__bson_float_swap_slow(v)) +#define BSON_FLOAT_TO_LE(v) (__bson_float_swap_slow(v)) #else #error "The endianness of target architecture is unknown." #endif @@ -130,9 +124,9 @@ BSON_BEGIN_DECLS */ static BSON_INLINE uint16_t -__bson_uint16_swap_slow (uint16_t v) /* IN */ +__bson_uint16_swap_slow(uint16_t v) /* IN */ { - return (uint16_t) ((v & 0x00FF) << 8) | (uint16_t) ((v & 0xFF00) >> 8); + return (uint16_t)((v & 0x00FF) << 8) | (uint16_t)((v & 0xFF00) >> 8); } @@ -153,7 +147,7 @@ __bson_uint16_swap_slow (uint16_t v) /* IN */ */ static BSON_INLINE uint32_t -__bson_uint32_swap_slow (uint32_t v) /* IN */ +__bson_uint32_swap_slow(uint32_t v) /* IN */ { return ((v & 0x000000FFU) << 24) | ((v & 0x0000FF00U) << 8) | ((v & 0x00FF0000U) >> 8) | ((v & 0xFF000000U) >> 24); } @@ -176,7 +170,7 @@ __bson_uint32_swap_slow (uint32_t v) /* IN */ */ static BSON_INLINE uint64_t -__bson_uint64_swap_slow (uint64_t v) /* IN */ +__bson_uint64_swap_slow(uint64_t v) /* IN */ { return ((v & 0x00000000000000FFULL) << 56) | ((v & 0x000000000000FF00ULL) << 40) | ((v & 0x0000000000FF0000ULL) << 24) | ((v & 0x00000000FF000000ULL) << 8) | @@ -201,16 +195,47 @@ __bson_uint64_swap_slow (uint64_t v) /* IN */ *-------------------------------------------------------------------------- */ -BSON_STATIC_ASSERT2 (sizeof_uint64_t, sizeof (double) == sizeof (uint64_t)); +BSON_STATIC_ASSERT2(sizeof_uint64_t, sizeof(double) == sizeof(uint64_t)); static BSON_INLINE double -__bson_double_swap_slow (double v) /* IN */ +__bson_double_swap_slow(double v) /* IN */ { uint64_t uv; - memcpy (&uv, &v, sizeof (v)); - uv = BSON_UINT64_SWAP_LE_BE (uv); - memcpy (&v, &uv, sizeof (v)); + memcpy(&uv, &v, sizeof(v)); + uv = BSON_UINT64_SWAP_LE_BE(uv); + memcpy(&v, &uv, sizeof(v)); + + return v; +} + + +/* + *-------------------------------------------------------------------------- + * + * __bson_float_swap_slow -- + * + * Fallback endianness conversion for single floating point. + * + * Returns: + * The endian swapped version. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +BSON_STATIC_ASSERT2(sizeof_uint32_t, sizeof(float) == sizeof(uint32_t)); + +static BSON_INLINE float +__bson_float_swap_slow(float v) /* IN */ +{ + uint32_t uv; + + memcpy(&uv, &v, sizeof(v)); + uv = BSON_UINT32_SWAP_LE_BE(uv); + memcpy(&v, &uv, sizeof(v)); return v; } diff --git a/bsonjs/bson/bson-error.h b/bsonjs/bson/bson-error-private.h similarity index 53% rename from bsonjs/bson/bson-error.h rename to bsonjs/bson/bson-error-private.h index 7d17b84..32f4f6b 100644 --- a/bsonjs/bson/bson-error.h +++ b/bsonjs/bson/bson-error-private.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,31 +16,24 @@ #include +#ifndef BSON_ERROR_PRIVATE_H +#define BSON_ERROR_PRIVATE_H -#ifndef BSON_ERROR_H -#define BSON_ERROR_H +#include // IWYU pragma: export +// -#include -#include -#include +#include -BSON_BEGIN_DECLS +#define BSON_ERROR_CATEGORY 1 -#define BSON_ERROR_JSON 1 -#define BSON_ERROR_READER 2 -#define BSON_ERROR_INVALID 3 +static BSON_INLINE void +bson_set_error_category(bson_error_t *error, uint8_t category) +{ + BSON_ASSERT_PARAM(error); + error->reserved = category; +} - -BSON_EXPORT (void) -bson_set_error (bson_error_t *error, uint32_t domain, uint32_t code, const char *format, ...) BSON_GNUC_PRINTF (4, 5); -BSON_EXPORT (char *) -bson_strerror_r (int err_code, char *buf, size_t buflen); - - -BSON_END_DECLS - - -#endif /* BSON_ERROR_H */ +#endif /* BSON_ERROR_PRIVATE_H */ diff --git a/bsonjs/bson/bson-iso8601-private.h b/bsonjs/bson/bson-iso8601-private.h index 81909a7..0ca5e47 100644 --- a/bsonjs/bson/bson-iso8601-private.h +++ b/bsonjs/bson/bson-iso8601-private.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,25 +21,16 @@ #define BSON_ISO8601_PRIVATE_H -#include -#include -#include +#include + +#include +#include BSON_BEGIN_DECLS bool -_bson_iso8601_date_parse (const char *str, int32_t len, int64_t *out, bson_error_t *error); - -/** - * _bson_iso8601_date_format: - * @msecs_since_epoch: A positive number of milliseconds since Jan 1, 1970. - * @str: The string to append the ISO8601-formatted to. - * - * Appends a date formatted like "2012-12-24T12:15:30.500Z" to @str. - */ -void -_bson_iso8601_date_format (int64_t msecs_since_epoch, bson_string_t *str); +_bson_iso8601_date_parse(const char *str, int32_t len, int64_t *out, bson_error_t *error); BSON_END_DECLS diff --git a/bsonjs/bson/bson-iso8601.c b/bsonjs/bson/bson-iso8601.c index 47687ce..8175e96 100644 --- a/bsonjs/bson/bson-iso8601.c +++ b/bsonjs/bson/bson-iso8601.c @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,16 +15,17 @@ */ -#include -#include -#include #include -#include #include +#include +#include +#include +#include + static bool -get_tok (const char *terminals, const char **ptr, int32_t *remaining, const char **out, int32_t *out_len) +get_tok(const char *terminals, const char **ptr, int32_t *remaining, const char **out, int32_t *out_len) { const char *terminal; bool found_terminal = false; @@ -54,12 +55,12 @@ get_tok (const char *terminals, const char **ptr, int32_t *remaining, const char } static bool -digits_only (const char *str, int32_t len) +digits_only(const char *str, int32_t len) { int i; for (i = 0; i < len; i++) { - if (!isdigit (str[i])) { + if (!isdigit(str[i])) { return false; } } @@ -68,13 +69,13 @@ digits_only (const char *str, int32_t len) } static bool -parse_num (const char *str, int32_t len, int32_t digits, int32_t min, int32_t max, int32_t *out) +parse_num(const char *str, int32_t len, int32_t digits, int32_t min, int32_t max, int32_t *out) { int i; int magnitude = 1; int32_t value = 0; - if ((digits >= 0 && len != digits) || !digits_only (str, len)) { + if ((digits >= 0 && len != digits) || !digits_only(str, len)) { return false; } @@ -92,7 +93,7 @@ parse_num (const char *str, int32_t len, int32_t digits, int32_t min, int32_t ma } bool -_bson_iso8601_date_parse (const char *str, int32_t len, int64_t *out, bson_error_t *error) +_bson_iso8601_date_parse(const char *str, int32_t len, int64_t *out, bson_error_t *error) { const char *ptr; int32_t remaining = len; @@ -127,47 +128,47 @@ _bson_iso8601_date_parse (const char *str, int32_t len, int64_t *out, bson_error struct bson_tm posix_date = {0}; #define DATE_PARSE_ERR(msg) \ - bson_set_error ( \ + bson_set_error( \ error, BSON_ERROR_JSON, BSON_JSON_ERROR_READ_INVALID_PARAM, "Could not parse \"%s\" as date: " msg, str); \ return false -#define DEFAULT_DATE_PARSE_ERR \ - DATE_PARSE_ERR ("use ISO8601 format yyyy-mm-ddThh:mm plus timezone, either" \ - " \"Z\" or like \"+0500\" or like \"+05:00\"") +#define DEFAULT_DATE_PARSE_ERR \ + DATE_PARSE_ERR("use ISO8601 format yyyy-mm-ddThh:mm plus timezone, either" \ + " \"Z\" or like \"+0500\" or like \"+05:00\"") ptr = str; /* we have to match at least yyyy-mm-ddThh:mm */ - if (!(get_tok ("-", &ptr, &remaining, &year_ptr, &year_len) && - get_tok ("-", &ptr, &remaining, &month_ptr, &month_len) && - get_tok ("T", &ptr, &remaining, &day_ptr, &day_len) && get_tok (":", &ptr, &remaining, &hour_ptr, &hour_len) && - get_tok (":+-Z", &ptr, &remaining, &min_ptr, &min_len))) { + if (!(get_tok("-", &ptr, &remaining, &year_ptr, &year_len) && + get_tok("-", &ptr, &remaining, &month_ptr, &month_len) && get_tok("T", &ptr, &remaining, &day_ptr, &day_len) && + get_tok(":", &ptr, &remaining, &hour_ptr, &hour_len) && + get_tok(":+-Z", &ptr, &remaining, &min_ptr, &min_len))) { DEFAULT_DATE_PARSE_ERR; } /* if the minute has a ':' at the end look for seconds */ if (min_ptr[min_len] == ':') { if (remaining < 2) { - DATE_PARSE_ERR ("reached end of date while looking for seconds"); + DATE_PARSE_ERR("reached end of date while looking for seconds"); } - get_tok (".+-Z", &ptr, &remaining, &sec_ptr, &sec_len); + get_tok(".+-Z", &ptr, &remaining, &sec_ptr, &sec_len); if (!sec_len) { - DATE_PARSE_ERR ("minute ends in \":\" seconds is required"); + DATE_PARSE_ERR("minute ends in \":\" seconds is required"); } } /* if we had a second and it is followed by a '.' look for milliseconds */ if (sec_len && sec_ptr[sec_len] == '.') { if (remaining < 2) { - DATE_PARSE_ERR ("reached end of date while looking for milliseconds"); + DATE_PARSE_ERR("reached end of date while looking for milliseconds"); } - get_tok ("+-Z", &ptr, &remaining, &millis_ptr, &millis_len); + get_tok("+-Z", &ptr, &remaining, &millis_ptr, &millis_len); if (!millis_len) { - DATE_PARSE_ERR ("seconds ends in \".\", milliseconds is required"); + DATE_PARSE_ERR("seconds ends in \".\", milliseconds is required"); } } @@ -175,36 +176,36 @@ _bson_iso8601_date_parse (const char *str, int32_t len, int64_t *out, bson_error ptr--; remaining++; - get_tok ("", &ptr, &remaining, &tz_ptr, &tz_len); + get_tok("", &ptr, &remaining, &tz_ptr, &tz_len); - if (!parse_num (year_ptr, year_len, 4, -9999, 9999, &year)) { - DATE_PARSE_ERR ("year must be an integer"); + if (!parse_num(year_ptr, year_len, 4, -9999, 9999, &year)) { + DATE_PARSE_ERR("year must be an integer"); } /* values are as in struct tm */ year -= 1900; - if (!parse_num (month_ptr, month_len, 2, 1, 12, &month)) { - DATE_PARSE_ERR ("month must be an integer"); + if (!parse_num(month_ptr, month_len, 2, 1, 12, &month)) { + DATE_PARSE_ERR("month must be an integer"); } /* values are as in struct tm */ month -= 1; - if (!parse_num (day_ptr, day_len, 2, 1, 31, &day)) { - DATE_PARSE_ERR ("day must be an integer"); + if (!parse_num(day_ptr, day_len, 2, 1, 31, &day)) { + DATE_PARSE_ERR("day must be an integer"); } - if (!parse_num (hour_ptr, hour_len, 2, 0, 23, &hour)) { - DATE_PARSE_ERR ("hour must be an integer"); + if (!parse_num(hour_ptr, hour_len, 2, 0, 23, &hour)) { + DATE_PARSE_ERR("hour must be an integer"); } - if (!parse_num (min_ptr, min_len, 2, 0, 59, &min)) { - DATE_PARSE_ERR ("minute must be an integer"); + if (!parse_num(min_ptr, min_len, 2, 0, 59, &min)) { + DATE_PARSE_ERR("minute must be an integer"); } - if (sec_len && !parse_num (sec_ptr, sec_len, 2, 0, 60, &sec)) { - DATE_PARSE_ERR ("seconds must be an integer"); + if (sec_len && !parse_num(sec_ptr, sec_len, 2, 0, 60, &sec)) { + DATE_PARSE_ERR("seconds must be an integer"); } if (tz_len > 0) { @@ -214,18 +215,18 @@ _bson_iso8601_date_parse (const char *str, int32_t len, int64_t *out, bson_error int32_t tz_hour; int32_t tz_min; - if ((tz_len != 5 || !digits_only (tz_ptr + 1, 4)) && - (tz_len != 6 || !digits_only (tz_ptr + 1, 2) || tz_ptr[3] != ':' || !digits_only (tz_ptr + 4, 2))) { - DATE_PARSE_ERR ("could not parse timezone"); + if ((tz_len != 5 || !digits_only(tz_ptr + 1, 4)) && + (tz_len != 6 || !digits_only(tz_ptr + 1, 2) || tz_ptr[3] != ':' || !digits_only(tz_ptr + 4, 2))) { + DATE_PARSE_ERR("could not parse timezone"); } - if (!parse_num (tz_ptr + 1, 2, -1, -23, 23, &tz_hour)) { - DATE_PARSE_ERR ("timezone hour must be at most 23"); + if (!parse_num(tz_ptr + 1, 2, -1, -23, 23, &tz_hour)) { + DATE_PARSE_ERR("timezone hour must be at most 23"); } int32_t tz_min_offset = tz_ptr[3] == ':' ? 1 : 0; - if (!parse_num (tz_ptr + 3 + tz_min_offset, 2, -1, 0, 59, &tz_min)) { - DATE_PARSE_ERR ("timezone minute must be at most 59"); + if (!parse_num(tz_ptr + 3 + tz_min_offset, 2, -1, 0, 59, &tz_min)) { + DATE_PARSE_ERR("timezone minute must be at most 59"); } /* we inflect the meaning of a 'positive' timezone. Those are hours @@ -233,10 +234,10 @@ _bson_iso8601_date_parse (const char *str, int32_t len, int64_t *out, bson_error tz_adjustment = (tz_ptr[0] == '-' ? 1 : -1) * ((tz_min * 60) + (tz_hour * 60 * 60)); if (!(tz_adjustment > -86400 && tz_adjustment < 86400)) { - DATE_PARSE_ERR ("timezone offset must be less than 24 hours"); + DATE_PARSE_ERR("timezone offset must be less than 24 hours"); } } else { - DATE_PARSE_ERR ("timezone is required"); + DATE_PARSE_ERR("timezone is required"); } } @@ -245,8 +246,8 @@ _bson_iso8601_date_parse (const char *str, int32_t len, int64_t *out, bson_error int magnitude; millis = 0; - if (millis_len > 3 || !digits_only (millis_ptr, millis_len)) { - DATE_PARSE_ERR ("milliseconds must be an integer"); + if (millis_len > 3 || !digits_only(millis_ptr, millis_len)) { + DATE_PARSE_ERR("milliseconds must be an integer"); } for (i = 1, magnitude = 1; i <= millis_len; i++, magnitude *= 10) { @@ -260,7 +261,7 @@ _bson_iso8601_date_parse (const char *str, int32_t len, int64_t *out, bson_error } if (millis < 0 || millis > 1000) { - DATE_PARSE_ERR ("milliseconds must be at least 0 and less than 1000"); + DATE_PARSE_ERR("milliseconds must be at least 0 and less than 1000"); } } @@ -273,45 +274,9 @@ _bson_iso8601_date_parse (const char *str, int32_t len, int64_t *out, bson_error posix_date.tm_wday = 0; posix_date.tm_yday = 0; - millis = 1000 * _bson_timegm (&posix_date) + millis; + millis = 1000 * _bson_timegm(&posix_date) + millis; millis += tz_adjustment * 1000; *out = millis; return true; } - - -void -_bson_iso8601_date_format (int64_t msec_since_epoch, bson_string_t *str) -{ - time_t t; - int64_t msecs_part; - char buf[64]; - - msecs_part = msec_since_epoch % 1000; - t = (time_t) (msec_since_epoch / 1000); - -#ifdef BSON_HAVE_GMTIME_R - { - struct tm posix_date; - gmtime_r (&t, &posix_date); - strftime (buf, sizeof buf, "%Y-%m-%dT%H:%M:%S", &posix_date); - } -#elif defined(_MSC_VER) - { - /* Windows gmtime_s is thread-safe */ - struct tm time_buf; - gmtime_s (&time_buf, &t); - strftime (buf, sizeof buf, "%Y-%m-%dT%H:%M:%S", &time_buf); - } -#else - strftime (buf, sizeof buf, "%Y-%m-%dT%H:%M:%S", gmtime (&t)); -#endif - - if (msecs_part) { - bson_string_append_printf (str, "%s.%03" PRId64 "Z", buf, msecs_part); - } else { - bson_string_append (str, buf); - bson_string_append_c (str, 'Z'); - } -} diff --git a/bsonjs/bson/bson-iter.c b/bsonjs/bson/bson-iter.c index 0925b02..cec8730 100644 --- a/bsonjs/bson/bson-iter.c +++ b/bsonjs/bson/bson-iter.c @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,13 @@ #include -#include + #include #include +#include + +#include +#include #define ITER_TYPE(i) ((bson_type_t) * ((i)->raw + (i)->type)) @@ -40,18 +44,18 @@ */ bool -bson_iter_init (bson_iter_t *iter, /* OUT */ - const bson_t *bson) /* IN */ +bson_iter_init(bson_iter_t *iter, /* OUT */ + const bson_t *bson) /* IN */ { - BSON_ASSERT (iter); - BSON_ASSERT (bson); + BSON_ASSERT(iter); + BSON_ASSERT(bson); - if (BSON_UNLIKELY (bson->len < 5)) { - memset (iter, 0, sizeof *iter); + if (BSON_UNLIKELY(bson->len < 5)) { + memset(iter, 0, sizeof *iter); return false; } - iter->raw = bson_get_data (bson); + iter->raw = bson_get_data(bson); iter->len = bson->len; iter->off = 0; iter->type = 0; @@ -62,6 +66,7 @@ bson_iter_init (bson_iter_t *iter, /* OUT */ iter->d4 = 0; iter->next_off = 4; iter->err_off = 0; + iter->value = (bson_value_t){0}; return true; } @@ -84,39 +89,37 @@ bson_iter_init (bson_iter_t *iter, /* OUT */ */ bool -bson_iter_init_from_data (bson_iter_t *iter, /* OUT */ - const uint8_t *data, /* IN */ - size_t length) /* IN */ +bson_iter_init_from_data(bson_iter_t *iter, /* OUT */ + const uint8_t *data, /* IN */ + size_t length) /* IN */ { - uint32_t len_le; - - BSON_ASSERT (iter); - BSON_ASSERT (data); + BSON_ASSERT(iter); + BSON_ASSERT(data); - if (BSON_UNLIKELY ((length < 5) || (length > INT_MAX))) { - memset (iter, 0, sizeof *iter); + if (BSON_UNLIKELY((length < 5) || (length > INT_MAX))) { + memset(iter, 0, sizeof *iter); return false; } - memcpy (&len_le, data, sizeof (len_le)); - - if (BSON_UNLIKELY ((size_t) BSON_UINT32_FROM_LE (len_le) != length)) { - memset (iter, 0, sizeof *iter); + // Check that the object header matches the length of the given buffer + const uint32_t hdr_len = mlib_read_u32le(data); + if (BSON_UNLIKELY(hdr_len != length)) { + memset(iter, 0, sizeof *iter); return false; } - if (BSON_UNLIKELY (data[length - 1])) { - memset (iter, 0, sizeof *iter); + if (BSON_UNLIKELY(data[length - 1])) { + memset(iter, 0, sizeof *iter); return false; } - if (BSON_UNLIKELY (!bson_in_range_unsigned (uint32_t, length))) { - memset (iter, 0, sizeof *iter); + if (BSON_UNLIKELY(!mlib_in_range(uint32_t, length))) { + memset(iter, 0, sizeof *iter); return false; } - iter->raw = (uint8_t *) data; - iter->len = (uint32_t) length; + iter->raw = (uint8_t *)data; + iter->len = (uint32_t)length; iter->off = 0; iter->type = 0; iter->key = 0; @@ -149,19 +152,19 @@ bson_iter_init_from_data (bson_iter_t *iter, /* OUT */ */ bool -bson_iter_recurse (const bson_iter_t *iter, /* IN */ - bson_iter_t *child) /* OUT */ +bson_iter_recurse(const bson_iter_t *iter, /* IN */ + bson_iter_t *child) /* OUT */ { const uint8_t *data = NULL; uint32_t len = 0; - BSON_ASSERT (iter); - BSON_ASSERT (child); + BSON_ASSERT(iter); + BSON_ASSERT(child); - if (ITER_TYPE (iter) == BSON_TYPE_DOCUMENT) { - bson_iter_document (iter, &len, &data); - } else if (ITER_TYPE (iter) == BSON_TYPE_ARRAY) { - bson_iter_array (iter, &len, &data); + if (ITER_TYPE(iter) == BSON_TYPE_DOCUMENT) { + bson_iter_document(iter, &len, &data); + } else if (ITER_TYPE(iter) == BSON_TYPE_ARRAY) { + bson_iter_array(iter, &len, &data); } else { return false; } @@ -200,15 +203,15 @@ bson_iter_recurse (const bson_iter_t *iter, /* IN */ */ bool -bson_iter_init_find (bson_iter_t *iter, /* INOUT */ - const bson_t *bson, /* IN */ - const char *key) /* IN */ +bson_iter_init_find(bson_iter_t *iter, /* INOUT */ + const bson_t *bson, /* IN */ + const char *key) /* IN */ { - BSON_ASSERT (iter); - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT(iter); + BSON_ASSERT(bson); + BSON_ASSERT(key); - return bson_iter_init (iter, bson) && bson_iter_find (iter, key); + return bson_iter_init(iter, bson) && bson_iter_find(iter, key); } @@ -230,16 +233,16 @@ bson_iter_init_find (bson_iter_t *iter, /* INOUT */ */ bool -bson_iter_init_find_w_len (bson_iter_t *iter, /* INOUT */ - const bson_t *bson, /* IN */ - const char *key, /* IN */ - int keylen) /* IN */ +bson_iter_init_find_w_len(bson_iter_t *iter, /* INOUT */ + const bson_t *bson, /* IN */ + const char *key, /* IN */ + int keylen) /* IN */ { - BSON_ASSERT (iter); - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT(iter); + BSON_ASSERT(bson); + BSON_ASSERT(key); - return bson_iter_init (iter, bson) && bson_iter_find_w_len (iter, key, keylen); + return bson_iter_init(iter, bson) && bson_iter_find_w_len(iter, key, keylen); } @@ -260,15 +263,15 @@ bson_iter_init_find_w_len (bson_iter_t *iter, /* INOUT */ */ bool -bson_iter_init_find_case (bson_iter_t *iter, /* INOUT */ - const bson_t *bson, /* IN */ - const char *key) /* IN */ +bson_iter_init_find_case(bson_iter_t *iter, /* INOUT */ + const bson_t *bson, /* IN */ + const char *key) /* IN */ { - BSON_ASSERT (iter); - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT(iter); + BSON_ASSERT(bson); + BSON_ASSERT(key); - return bson_iter_init (iter, bson) && bson_iter_find_case (iter, key); + return bson_iter_init(iter, bson) && bson_iter_find_case(iter, key); } @@ -291,20 +294,20 @@ bson_iter_init_find_case (bson_iter_t *iter, /* INOUT */ */ bool -bson_iter_find_w_len (bson_iter_t *iter, /* INOUT */ - const char *key, /* IN */ - int keylen) /* IN */ +bson_iter_find_w_len(bson_iter_t *iter, /* INOUT */ + const char *key, /* IN */ + int keylen) /* IN */ { const char *ikey; if (keylen < 0) { - keylen = (int) strlen (key); + keylen = (int)strlen(key); } - while (bson_iter_next (iter)) { - ikey = bson_iter_key (iter); + while (bson_iter_next(iter)) { + ikey = bson_iter_key(iter); - if ((0 == strncmp (key, ikey, keylen)) && (ikey[keylen] == '\0')) { + if ((0 == strncmp(key, ikey, keylen)) && (ikey[keylen] == '\0')) { return true; } } @@ -332,13 +335,13 @@ bson_iter_find_w_len (bson_iter_t *iter, /* INOUT */ */ bool -bson_iter_find (bson_iter_t *iter, /* INOUT */ - const char *key) /* IN */ +bson_iter_find(bson_iter_t *iter, /* INOUT */ + const char *key) /* IN */ { - BSON_ASSERT (iter); - BSON_ASSERT (key); + BSON_ASSERT(iter); + BSON_ASSERT(key); - return bson_iter_find_w_len (iter, key, -1); + return bson_iter_find_w_len(iter, key, -1); } @@ -361,14 +364,14 @@ bson_iter_find (bson_iter_t *iter, /* INOUT */ */ bool -bson_iter_find_case (bson_iter_t *iter, /* INOUT */ - const char *key) /* IN */ +bson_iter_find_case(bson_iter_t *iter, /* INOUT */ + const char *key) /* IN */ { - BSON_ASSERT (iter); - BSON_ASSERT (key); + BSON_ASSERT(iter); + BSON_ASSERT(key); - while (bson_iter_next (iter)) { - if (!bson_strcasecmp (key, bson_iter_key (iter))) { + while (bson_iter_next(iter)) { + if (!bson_strcasecmp(key, bson_iter_key(iter))) { return true; } } @@ -396,33 +399,33 @@ bson_iter_find_case (bson_iter_t *iter, /* INOUT */ */ bool -bson_iter_find_descendant (bson_iter_t *iter, /* INOUT */ - const char *dotkey, /* IN */ - bson_iter_t *descendant) /* OUT */ +bson_iter_find_descendant(bson_iter_t *iter, /* INOUT */ + const char *dotkey, /* IN */ + bson_iter_t *descendant) /* OUT */ { bson_iter_t tmp; const char *dot; size_t sublen; - BSON_ASSERT (iter); - BSON_ASSERT (dotkey); - BSON_ASSERT (descendant); + BSON_ASSERT(iter); + BSON_ASSERT(dotkey); + BSON_ASSERT(descendant); - if ((dot = strchr (dotkey, '.'))) { + if ((dot = strchr(dotkey, '.'))) { sublen = dot - dotkey; } else { - sublen = strlen (dotkey); + sublen = strlen(dotkey); } - if (bson_iter_find_w_len (iter, dotkey, (int) sublen)) { + if (bson_iter_find_w_len(iter, dotkey, (int)sublen)) { if (!dot) { *descendant = *iter; return true; } - if (BSON_ITER_HOLDS_DOCUMENT (iter) || BSON_ITER_HOLDS_ARRAY (iter)) { - if (bson_iter_recurse (iter, &tmp)) { - return bson_iter_find_descendant (&tmp, dot + 1, descendant); + if (BSON_ITER_HOLDS_DOCUMENT(iter) || BSON_ITER_HOLDS_ARRAY(iter)) { + if (bson_iter_recurse(iter, &tmp)) { + return bson_iter_find_descendant(&tmp, dot + 1, descendant); } } } @@ -449,11 +452,11 @@ bson_iter_find_descendant (bson_iter_t *iter, /* INOUT */ */ const char * -bson_iter_key (const bson_iter_t *iter) /* IN */ +bson_iter_key(const bson_iter_t *iter) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - return bson_iter_key_unsafe (iter); + return bson_iter_key_unsafe(iter); } @@ -475,13 +478,13 @@ bson_iter_key (const bson_iter_t *iter) /* IN */ */ bson_type_t -bson_iter_type (const bson_iter_t *iter) /* IN */ +bson_iter_type(const bson_iter_t *iter) /* IN */ { - BSON_ASSERT (iter); - BSON_ASSERT (iter->raw); - BSON_ASSERT (iter->len); + BSON_ASSERT(iter); + BSON_ASSERT(iter->raw); + BSON_ASSERT(iter->len); - return bson_iter_type_unsafe (iter); + return bson_iter_type_unsafe(iter); } @@ -511,17 +514,17 @@ bson_iter_type (const bson_iter_t *iter) /* IN */ */ static bool -_bson_iter_next_internal (bson_iter_t *iter, /* INOUT */ - uint32_t next_keylen, /* IN */ - const char **key, /* OUT */ - uint32_t *bson_type, /* OUT */ - bool *unsupported) /* OUT */ +_bson_iter_next_internal(bson_iter_t *iter, /* INOUT */ + uint32_t next_keylen, /* IN */ + const char **key, /* OUT */ + uint32_t *bson_type, /* OUT */ + bool *unsupported) /* OUT */ { const uint8_t *data; uint32_t o; unsigned int len; - BSON_ASSERT (iter); + BSON_ASSERT(iter); *unsupported = false; @@ -560,8 +563,8 @@ _bson_iter_next_internal (bson_iter_t *iter, /* INOUT */ fill_data_fields: - *key = bson_iter_key_unsafe (iter); - *bson_type = ITER_TYPE (iter); + *key = bson_iter_key_unsafe(iter); + *bson_type = ITER_TYPE(iter); switch (*bson_type) { case BSON_TYPE_DATE_TIME: @@ -573,16 +576,13 @@ _bson_iter_next_internal (bson_iter_t *iter, /* INOUT */ case BSON_TYPE_CODE: case BSON_TYPE_SYMBOL: case BSON_TYPE_UTF8: { - uint32_t l; - if ((o + 4) >= len) { iter->err_off = o; goto mark_invalid; } iter->d2 = o + 4; - memcpy (&l, iter->raw + iter->d1, sizeof (l)); - l = BSON_UINT32_FROM_LE (l); + const uint32_t l = mlib_read_u32le(iter->raw + iter->d1); if (l > (len - (o + 4))) { iter->err_off = o; @@ -594,7 +594,7 @@ _bson_iter_next_internal (bson_iter_t *iter, /* INOUT */ /* * Make sure the string length includes the NUL byte. */ - if (BSON_UNLIKELY ((l == 0) || (iter->next_off >= len))) { + if (BSON_UNLIKELY((l == 0) || (iter->next_off >= len))) { iter->err_off = o; goto mark_invalid; } @@ -602,14 +602,13 @@ _bson_iter_next_internal (bson_iter_t *iter, /* INOUT */ /* * Make sure the last byte is a NUL byte. */ - if (BSON_UNLIKELY ((iter->raw + iter->d2)[l - 1] != '\0')) { + if (BSON_UNLIKELY((iter->raw + iter->d2)[l - 1] != '\0')) { iter->err_off = o + 4 + l - 1; goto mark_invalid; } } break; case BSON_TYPE_BINARY: { bson_subtype_t subtype; - uint32_t l; if (o >= (len - 4)) { iter->err_off = o; @@ -619,8 +618,7 @@ _bson_iter_next_internal (bson_iter_t *iter, /* INOUT */ iter->d2 = o + 4; iter->d3 = o + 5; - memcpy (&l, iter->raw + iter->d1, sizeof (l)); - l = BSON_UINT32_FROM_LE (l); + const uint32_t l = mlib_read_u32le(iter->raw + iter->d1); if (l >= (len - o - 4)) { iter->err_off = o; @@ -630,17 +628,14 @@ _bson_iter_next_internal (bson_iter_t *iter, /* INOUT */ subtype = *(iter->raw + iter->d2); if (subtype == BSON_SUBTYPE_BINARY_DEPRECATED) { - int32_t binary_len; - if (l < 4) { iter->err_off = o; goto mark_invalid; } /* subtype 2 has a redundant length header in the data */ - memcpy (&binary_len, (iter->raw + iter->d3), sizeof (binary_len)); - binary_len = BSON_UINT32_FROM_LE (binary_len); - if (binary_len + 4 != l) { + const int32_t bin_len = mlib_read_i32le(iter->raw + iter->d3); + if (mlib_cmp(bin_len, !=, l - 4)) { iter->err_off = iter->d3; goto mark_invalid; } @@ -650,16 +645,12 @@ _bson_iter_next_internal (bson_iter_t *iter, /* INOUT */ } break; case BSON_TYPE_ARRAY: case BSON_TYPE_DOCUMENT: { - uint32_t l; - if (o >= (len - 4)) { iter->err_off = o; goto mark_invalid; } - memcpy (&l, iter->raw + iter->d1, sizeof (l)); - l = BSON_UINT32_FROM_LE (l); - + const uint32_t l = mlib_read_u32le(iter->raw + iter->d1); if ((l > len) || (l > (len - o))) { iter->err_off = o; goto mark_invalid; @@ -678,7 +669,7 @@ _bson_iter_next_internal (bson_iter_t *iter, /* INOUT */ goto mark_invalid; } - memcpy (&val, iter->raw + iter->d1, 1); + memcpy(&val, iter->raw + iter->d1, 1); if (val != 0x00 && val != 0x01) { iter->err_off = o; goto mark_invalid; @@ -718,16 +709,13 @@ _bson_iter_next_internal (bson_iter_t *iter, /* INOUT */ iter->next_off = o + 1; } break; case BSON_TYPE_DBPOINTER: { - uint32_t l; - if (o >= (len - 4)) { iter->err_off = o; goto mark_invalid; } iter->d2 = o + 4; - memcpy (&l, iter->raw + iter->d1, sizeof (l)); - l = BSON_UINT32_FROM_LE (l); + const uint32_t l = mlib_read_u32le(iter->raw + iter->d1); /* Check valid string length. l counts '\0' but not 4 bytes for itself. */ if (l == 0 || l > (len - o - 4)) { @@ -745,9 +733,6 @@ _bson_iter_next_internal (bson_iter_t *iter, /* INOUT */ iter->next_off = o + 4 + l + 12; } break; case BSON_TYPE_CODEWSCOPE: { - uint32_t l; - uint32_t doclen; - if ((len < 19) || (o >= (len - 14))) { iter->err_off = o; goto mark_invalid; @@ -756,40 +741,37 @@ _bson_iter_next_internal (bson_iter_t *iter, /* INOUT */ iter->d2 = o + 4; iter->d3 = o + 8; - memcpy (&l, iter->raw + iter->d1, sizeof (l)); - l = BSON_UINT32_FROM_LE (l); + const uint32_t l1 = mlib_read_u32le(iter->raw + iter->d1); - if ((l < 14) || (l >= (len - o))) { + if ((l1 < 14) || (l1 >= (len - o))) { iter->err_off = o; goto mark_invalid; } - iter->next_off = o + l; + iter->next_off = o + l1; if (iter->next_off >= len) { iter->err_off = o; goto mark_invalid; } - memcpy (&l, iter->raw + iter->d2, sizeof (l)); - l = BSON_UINT32_FROM_LE (l); + const uint32_t l2 = mlib_read_u32le(iter->raw + iter->d2); - if (l == 0 || l >= (len - o - 4 - 4)) { + if (l2 == 0 || l2 >= (len - o - 4 - 4)) { iter->err_off = o; goto mark_invalid; } - if ((o + 4 + 4 + l + 4) >= iter->next_off) { + if ((o + 4 + 4 + l2 + 4) >= iter->next_off) { iter->err_off = o + 4; goto mark_invalid; } - iter->d4 = o + 4 + 4 + l; - memcpy (&doclen, iter->raw + iter->d4, sizeof (doclen)); - doclen = BSON_UINT32_FROM_LE (doclen); + iter->d4 = o + 4 + 4 + l2; + const uint32_t doclen = mlib_read_u32le(iter->raw + iter->d4); - if ((o + 4 + 4 + l + doclen) != iter->next_off) { - iter->err_off = o + 4 + 4 + l; + if ((o + 4 + 4 + l2 + doclen) != iter->next_off) { + iter->err_off = o + 4 + 4 + l2; goto mark_invalid; } } break; @@ -858,13 +840,13 @@ _bson_iter_next_internal (bson_iter_t *iter, /* INOUT */ */ bool -bson_iter_next (bson_iter_t *iter) /* INOUT */ +bson_iter_next(bson_iter_t *iter) /* INOUT */ { uint32_t bson_type; const char *key; bool unsupported; - return _bson_iter_next_internal (iter, 0, &key, &bson_type, &unsupported); + return _bson_iter_next_internal(iter, 0, &key, &bson_type, &unsupported); } @@ -879,6 +861,12 @@ bson_iter_next (bson_iter_t *iter) /* INOUT */ * @binary should not be modified or freed and is only valid while * @iter's bson_t is valid and unmodified. * + * Note: Public constraints are tighter than private ones. + * API documentation says it's "a programming error to call this function + * when ``iter`` is not observing an element of type BSON_TYPE_BINARY.". + * Privately only, we do check the iterator type and we output NULL and + * BSON_SUBTYPE_BINARY when the type is incorrect. + * * Parameters: * @iter: A bson_iter_t * @subtype: A location for the binary subtype. @@ -895,17 +883,17 @@ bson_iter_next (bson_iter_t *iter) /* INOUT */ */ void -bson_iter_binary (const bson_iter_t *iter, /* IN */ - bson_subtype_t *subtype, /* OUT */ - uint32_t *binary_len, /* OUT */ - const uint8_t **binary) /* OUT */ +bson_iter_binary(const bson_iter_t *iter, /* IN */ + bson_subtype_t *subtype, /* OUT */ + uint32_t *binary_len, /* OUT */ + const uint8_t **binary) /* OUT */ { bson_subtype_t backup; - BSON_ASSERT (iter); - BSON_ASSERT (!binary || binary_len); + BSON_ASSERT(iter); + BSON_ASSERT(!binary || binary_len); - if (ITER_TYPE (iter) == BSON_TYPE_BINARY) { + if (ITER_TYPE(iter) == BSON_TYPE_BINARY) { if (!subtype) { subtype = &backup; } @@ -913,13 +901,12 @@ bson_iter_binary (const bson_iter_t *iter, /* IN */ *subtype = (bson_subtype_t) * (iter->raw + iter->d2); if (binary) { - memcpy (binary_len, (iter->raw + iter->d1), sizeof (*binary_len)); - *binary_len = BSON_UINT32_FROM_LE (*binary_len); + *binary_len = mlib_read_u32le(iter->raw + iter->d1); *binary = iter->raw + iter->d3; if (*subtype == BSON_SUBTYPE_BINARY_DEPRECATED) { - *binary_len -= sizeof (int32_t); - *binary += sizeof (int32_t); + *binary_len -= sizeof(int32_t); + *binary += sizeof(int32_t); } } @@ -940,6 +927,149 @@ bson_iter_binary (const bson_iter_t *iter, /* IN */ } +/* + *-------------------------------------------------------------------------- + * + * bson_iter_overwrite_binary -- + * + * Obtain temporary mutable access to the contents of a BSON_TYPE_BINARY + * field. It may be modified in content only, without changing length + * or subtype, through a temporary pointer that's only valid until the + * underlying bson_t is modified or deleted. + * + * Note: Public constraints are tighter than private ones. + * API documentation says it's "a programming error to call this function + * when ``iter`` is not observing an element of type BSON_TYPE_BINARY.". + * Privately only, we do check the iterator type and we return NULL when + * the type is incorrect. + * + * Parameters: + * @iter: A bson_iter_t + * @binary_len: A location for the length of @binary. + * @binary: A location for a pointer to the binary data. + * + * Returns: + * On success, returns a pointer in *binary and a length in *binary_len. + * The pointer is invalidated when the underlying bson_t is destroyed or modified. + * If the iter does not point to a binary item of the indicated subtype, + * returns NULL in *binary and 0 *binary_len. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +BSON_EXPORT(void) +bson_iter_overwrite_binary(bson_iter_t *iter, /* IN */ + bson_subtype_t subtype, /* IN */ + uint32_t *binary_len, /* OUT */ + uint8_t **binary) /* OUT */ +{ + BSON_ASSERT_PARAM(iter); + BSON_OPTIONAL_PARAM(binary_len); + BSON_OPTIONAL_PARAM(binary); + + bson_subtype_t iter_subtype; + uint32_t iter_binary_len; + const uint8_t *iter_binary; + bson_iter_binary(iter, &iter_subtype, &iter_binary_len, &iter_binary); + + if (iter_binary && iter_subtype == subtype) { + // All of bson_iter_overwrite_* work by casting away const from iter->raw. + if (binary) { + *binary = (void *)iter_binary; + } + if (binary_len) { + *binary_len = iter_binary_len; + } + return; + } + if (binary) { + *binary = NULL; + } + if (binary_len) { + *binary_len = 0; + } +} + + +/* + *-------------------------------------------------------------------------- + * + * bson_iter_binary_subtype -- + * + * Retrieves the subtype of a BSON_TYPE_BINARY field. + * + * Note: Public constraints are tighter than private ones. + * API documentation says it's "a programming error to call this function + * when ``iter`` is not observing an element of type BSON_TYPE_BINARY.". + * Privately only, we do check the iterator type and return + * BSON_SUBTYPE_BINARY when the type is incorrect. + * + * Parameters: + * @iter: A bson_iter_t + * + * Returns: + * Same as the @subtype OUT parameter from bson_iter_binary(). + * If the iterator is valid, returns the referenced subtype. Otherwise, + * returns BSON_SUBTYPE_BINARY as a fallback. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +BSON_EXPORT(bson_subtype_t) +bson_iter_binary_subtype(const bson_iter_t *iter) +{ + bson_subtype_t result; + bson_iter_binary(iter, &result, NULL, NULL); + return result; +} + + +/* + *-------------------------------------------------------------------------- + * + * bson_iter_binary_equal -- + * + * Compare two BSON_TYPE_BINARY fields for equality. + * + * Parameters: + * @iter_a: First bson_iter_t to compare + * @iter_b: Second bson_iter_t to compare + * + * Returns: + * true if both iterators point to BSON_TYPE_BINARY fields with + * identical subtype and contents. false if there is any difference. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +BSON_EXPORT(bool) +bson_iter_binary_equal(const bson_iter_t *iter_a, const bson_iter_t *iter_b) +{ + BSON_ASSERT_PARAM(iter_a); + BSON_ASSERT_PARAM(iter_b); + + if (BSON_ITER_HOLDS_BINARY(iter_a) && BSON_ITER_HOLDS_BINARY(iter_b)) { + bson_subtype_t subtypes[2]; + uint32_t lengths[2]; + const uint8_t *data[2]; + bson_iter_binary(iter_a, &subtypes[0], &lengths[0], &data[0]); + bson_iter_binary(iter_b, &subtypes[1], &lengths[1], &data[1]); + return subtypes[0] == subtypes[1] && lengths[0] == lengths[1] && 0 == memcmp(data[0], data[1], lengths[0]); + } else { + return false; + } +} + + /* *-------------------------------------------------------------------------- * @@ -957,12 +1087,12 @@ bson_iter_binary (const bson_iter_t *iter, /* IN */ */ bool -bson_iter_bool (const bson_iter_t *iter) /* IN */ +bson_iter_bool(const bson_iter_t *iter) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_BOOL) { - return bson_iter_bool_unsafe (iter); + if (ITER_TYPE(iter) == BSON_TYPE_BOOL) { + return bson_iter_bool_unsafe(iter); } return false; @@ -990,19 +1120,19 @@ bson_iter_bool (const bson_iter_t *iter) /* IN */ */ bool -bson_iter_as_bool (const bson_iter_t *iter) /* IN */ +bson_iter_as_bool(const bson_iter_t *iter) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - switch ((int) ITER_TYPE (iter)) { + switch ((int)ITER_TYPE(iter)) { case BSON_TYPE_BOOL: - return bson_iter_bool (iter); + return bson_iter_bool(iter); case BSON_TYPE_DOUBLE: - return !(bson_iter_double (iter) == 0.0); + return !(bson_iter_double(iter) == 0.0); case BSON_TYPE_INT64: - return !(bson_iter_int64 (iter) == 0); + return !(bson_iter_int64(iter) == 0); case BSON_TYPE_INT32: - return !(bson_iter_int32 (iter) == 0); + return !(bson_iter_int32(iter) == 0); case BSON_TYPE_UTF8: return true; case BSON_TYPE_NULL: @@ -1031,12 +1161,12 @@ bson_iter_as_bool (const bson_iter_t *iter) /* IN */ */ double -bson_iter_double (const bson_iter_t *iter) /* IN */ +bson_iter_double(const bson_iter_t *iter) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_DOUBLE) { - return bson_iter_double_unsafe (iter); + if (ITER_TYPE(iter) == BSON_TYPE_DOUBLE) { + return bson_iter_double_unsafe(iter); } return 0; @@ -1064,19 +1194,19 @@ bson_iter_double (const bson_iter_t *iter) /* IN */ */ double -bson_iter_as_double (const bson_iter_t *iter) /* IN */ +bson_iter_as_double(const bson_iter_t *iter) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - switch ((int) ITER_TYPE (iter)) { + switch ((int)ITER_TYPE(iter)) { case BSON_TYPE_BOOL: - return (double) bson_iter_bool (iter); + return (double)bson_iter_bool(iter); case BSON_TYPE_DOUBLE: - return bson_iter_double (iter); + return bson_iter_double(iter); case BSON_TYPE_INT32: - return (double) bson_iter_int32 (iter); + return (double)bson_iter_int32(iter); case BSON_TYPE_INT64: - return (double) bson_iter_int64 (iter); + return (double)bson_iter_int64(iter); default: return 0; } @@ -1100,12 +1230,12 @@ bson_iter_as_double (const bson_iter_t *iter) /* IN */ */ int32_t -bson_iter_int32 (const bson_iter_t *iter) /* IN */ +bson_iter_int32(const bson_iter_t *iter) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_INT32) { - return bson_iter_int32_unsafe (iter); + if (ITER_TYPE(iter) == BSON_TYPE_INT32) { + return bson_iter_int32_unsafe(iter); } return 0; @@ -1129,12 +1259,12 @@ bson_iter_int32 (const bson_iter_t *iter) /* IN */ */ int64_t -bson_iter_int64 (const bson_iter_t *iter) /* IN */ +bson_iter_int64(const bson_iter_t *iter) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_INT64) { - return bson_iter_int64_unsafe (iter); + if (ITER_TYPE(iter) == BSON_TYPE_INT64) { + return bson_iter_int64_unsafe(iter); } return 0; @@ -1163,19 +1293,19 @@ bson_iter_int64 (const bson_iter_t *iter) /* IN */ */ int64_t -bson_iter_as_int64 (const bson_iter_t *iter) /* IN */ +bson_iter_as_int64(const bson_iter_t *iter) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - switch ((int) ITER_TYPE (iter)) { + switch ((int)ITER_TYPE(iter)) { case BSON_TYPE_BOOL: - return (int64_t) bson_iter_bool (iter); + return (int64_t)bson_iter_bool(iter); case BSON_TYPE_DOUBLE: - return (int64_t) bson_iter_double (iter); + return (int64_t)bson_iter_double(iter); case BSON_TYPE_INT64: - return bson_iter_int64 (iter); + return bson_iter_int64(iter); case BSON_TYPE_INT32: - return (int64_t) bson_iter_int32 (iter); + return (int64_t)bson_iter_int32(iter); default: return 0; } @@ -1201,13 +1331,13 @@ bson_iter_as_int64 (const bson_iter_t *iter) /* IN */ *-------------------------------------------------------------------------- */ bool -bson_iter_decimal128 (const bson_iter_t *iter, /* IN */ - bson_decimal128_t *dec) /* OUT */ +bson_iter_decimal128(const bson_iter_t *iter, /* IN */ + bson_decimal128_t *dec) /* OUT */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_DECIMAL128) { - bson_iter_decimal128_unsafe (iter, dec); + if (ITER_TYPE(iter) == BSON_TYPE_DECIMAL128) { + bson_iter_decimal128_unsafe(iter, dec); return true; } @@ -1233,12 +1363,12 @@ bson_iter_decimal128 (const bson_iter_t *iter, /* IN */ */ const bson_oid_t * -bson_iter_oid (const bson_iter_t *iter) /* IN */ +bson_iter_oid(const bson_iter_t *iter) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_OID) { - return bson_iter_oid_unsafe (iter); + if (ITER_TYPE(iter) == BSON_TYPE_OID) { + return bson_iter_oid_unsafe(iter); } return NULL; @@ -1263,17 +1393,17 @@ bson_iter_oid (const bson_iter_t *iter) /* IN */ */ const char * -bson_iter_regex (const bson_iter_t *iter, /* IN */ - const char **options) /* IN */ +bson_iter_regex(const bson_iter_t *iter, /* IN */ + const char **options) /* IN */ { const char *ret = NULL; const char *ret_options = NULL; - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_REGEX) { - ret = (const char *) (iter->raw + iter->d1); - ret_options = (const char *) (iter->raw + iter->d2); + if (ITER_TYPE(iter) == BSON_TYPE_REGEX) { + ret = (const char *)(iter->raw + iter->d1); + ret_options = (const char *)(iter->raw + iter->d2); } if (options) { @@ -1306,17 +1436,17 @@ bson_iter_regex (const bson_iter_t *iter, /* IN */ */ const char * -bson_iter_utf8 (const bson_iter_t *iter, /* IN */ - uint32_t *length) /* OUT */ +bson_iter_utf8(const bson_iter_t *iter, /* IN */ + uint32_t *length) /* OUT */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_UTF8) { + if (ITER_TYPE(iter) == BSON_TYPE_UTF8) { if (length) { - *length = bson_iter_utf8_len_unsafe (iter); + *length = bson_iter_utf8_len_unsafe(iter); } - return (const char *) (iter->raw + iter->d2); + return (const char *)(iter->raw + iter->d2); } if (length) { @@ -1346,18 +1476,18 @@ bson_iter_utf8 (const bson_iter_t *iter, /* IN */ */ char * -bson_iter_dup_utf8 (const bson_iter_t *iter, /* IN */ - uint32_t *length) /* OUT */ +bson_iter_dup_utf8(const bson_iter_t *iter, /* IN */ + uint32_t *length) /* OUT */ { uint32_t local_length = 0; const char *str; char *ret = NULL; - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if ((str = bson_iter_utf8 (iter, &local_length))) { - ret = bson_malloc0 (local_length + 1); - memcpy (ret, str, local_length); + if ((str = bson_iter_utf8(iter, &local_length))) { + ret = bson_malloc0(local_length + 1); + memcpy(ret, str, local_length); ret[local_length] = '\0'; } @@ -1392,17 +1522,17 @@ bson_iter_dup_utf8 (const bson_iter_t *iter, /* IN */ */ const char * -bson_iter_code (const bson_iter_t *iter, /* IN */ - uint32_t *length) /* OUT */ +bson_iter_code(const bson_iter_t *iter, /* IN */ + uint32_t *length) /* OUT */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_CODE) { + if (ITER_TYPE(iter) == BSON_TYPE_CODE) { if (length) { - *length = bson_iter_utf8_len_unsafe (iter); + *length = bson_iter_utf8_len_unsafe(iter); } - return (const char *) (iter->raw + iter->d2); + return (const char *)(iter->raw + iter->d2); } if (length) { @@ -1441,28 +1571,24 @@ bson_iter_code (const bson_iter_t *iter, /* IN */ */ const char * -bson_iter_codewscope (const bson_iter_t *iter, /* IN */ - uint32_t *length, /* OUT */ - uint32_t *scope_len, /* OUT */ - const uint8_t **scope) /* OUT */ +bson_iter_codewscope(const bson_iter_t *iter, /* IN */ + uint32_t *length, /* OUT */ + uint32_t *scope_len, /* OUT */ + const uint8_t **scope) /* OUT */ { - uint32_t len; + BSON_ASSERT(iter); - BSON_ASSERT (iter); - - if (ITER_TYPE (iter) == BSON_TYPE_CODEWSCOPE) { + if (ITER_TYPE(iter) == BSON_TYPE_CODEWSCOPE) { if (length) { - memcpy (&len, iter->raw + iter->d2, sizeof (len)); + const uint32_t len = mlib_read_u32le(iter->raw + iter->d2); /* The string length was checked > 0 in _bson_iter_next_internal. */ - len = BSON_UINT32_FROM_LE (len); - BSON_ASSERT (len > 0); + BSON_ASSERT(len > 0); *length = len - 1; } - memcpy (&len, iter->raw + iter->d4, sizeof (len)); - *scope_len = BSON_UINT32_FROM_LE (len); + *scope_len = mlib_read_u32le(iter->raw + iter->d4); *scope = iter->raw + iter->d4; - return (const char *) (iter->raw + iter->d3); + return (const char *)(iter->raw + iter->d3); } if (length) { @@ -1512,12 +1638,12 @@ bson_iter_codewscope (const bson_iter_t *iter, /* IN */ */ void -bson_iter_dbpointer (const bson_iter_t *iter, /* IN */ - uint32_t *collection_len, /* OUT */ - const char **collection, /* OUT */ - const bson_oid_t **oid) /* OUT */ +bson_iter_dbpointer(const bson_iter_t *iter, /* IN */ + uint32_t *collection_len, /* OUT */ + const char **collection, /* OUT */ + const bson_oid_t **oid) /* OUT */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); if (collection) { *collection = NULL; @@ -1527,10 +1653,9 @@ bson_iter_dbpointer (const bson_iter_t *iter, /* IN */ *oid = NULL; } - if (ITER_TYPE (iter) == BSON_TYPE_DBPOINTER) { + if (ITER_TYPE(iter) == BSON_TYPE_DBPOINTER) { if (collection_len) { - memcpy (collection_len, (iter->raw + iter->d1), sizeof (*collection_len)); - *collection_len = BSON_UINT32_FROM_LE (*collection_len); + *collection_len = mlib_read_u32le(iter->raw + iter->d1); if ((*collection_len) > 0) { (*collection_len)--; @@ -1538,11 +1663,11 @@ bson_iter_dbpointer (const bson_iter_t *iter, /* IN */ } if (collection) { - *collection = (const char *) (iter->raw + iter->d2); + *collection = (const char *)(iter->raw + iter->d2); } if (oid) { - *oid = (const bson_oid_t *) (iter->raw + iter->d3); + *oid = (const bson_oid_t *)(iter->raw + iter->d3); } } } @@ -1571,17 +1696,17 @@ bson_iter_dbpointer (const bson_iter_t *iter, /* IN */ */ const char * -bson_iter_symbol (const bson_iter_t *iter, /* IN */ - uint32_t *length) /* OUT */ +bson_iter_symbol(const bson_iter_t *iter, /* IN */ + uint32_t *length) /* OUT */ { const char *ret = NULL; uint32_t ret_length = 0; - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_SYMBOL) { - ret = (const char *) (iter->raw + iter->d2); - ret_length = bson_iter_utf8_len_unsafe (iter); + if (ITER_TYPE(iter) == BSON_TYPE_SYMBOL) { + ret = (const char *)(iter->raw + iter->d2); + ret_length = bson_iter_utf8_len_unsafe(iter); } if (length) { @@ -1610,12 +1735,12 @@ bson_iter_symbol (const bson_iter_t *iter, /* IN */ */ int64_t -bson_iter_date_time (const bson_iter_t *iter) /* IN */ +bson_iter_date_time(const bson_iter_t *iter) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_DATE_TIME) { - return bson_iter_int64_unsafe (iter); + if (ITER_TYPE(iter) == BSON_TYPE_DATE_TIME) { + return bson_iter_int64_unsafe(iter); } return 0; @@ -1640,12 +1765,12 @@ bson_iter_date_time (const bson_iter_t *iter) /* IN */ */ time_t -bson_iter_time_t (const bson_iter_t *iter) /* IN */ +bson_iter_time_t(const bson_iter_t *iter) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_DATE_TIME) { - return bson_iter_time_t_unsafe (iter); + if (ITER_TYPE(iter) == BSON_TYPE_DATE_TIME) { + return bson_iter_time_t_unsafe(iter); } return 0; @@ -1675,21 +1800,18 @@ bson_iter_time_t (const bson_iter_t *iter) /* IN */ */ void -bson_iter_timestamp (const bson_iter_t *iter, /* IN */ - uint32_t *timestamp, /* OUT */ - uint32_t *increment) /* OUT */ +bson_iter_timestamp(const bson_iter_t *iter, /* IN */ + uint32_t *timestamp, /* OUT */ + uint32_t *increment) /* OUT */ { - uint64_t encoded; uint32_t ret_timestamp = 0; uint32_t ret_increment = 0; - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_TIMESTAMP) { - memcpy (&encoded, iter->raw + iter->d1, sizeof (encoded)); - encoded = BSON_UINT64_FROM_LE (encoded); - ret_timestamp = (encoded >> 32) & 0xFFFFFFFF; - ret_increment = encoded & 0xFFFFFFFF; + if (ITER_TYPE(iter) == BSON_TYPE_TIMESTAMP) { + ret_increment = mlib_read_u32le(iter->raw + iter->d1); + ret_timestamp = mlib_read_u32le(iter->raw + iter->d1 + sizeof(uint32_t)); } if (timestamp) { @@ -1724,17 +1846,17 @@ bson_iter_timestamp (const bson_iter_t *iter, /* IN */ */ void -bson_iter_timeval (const bson_iter_t *iter, /* IN */ - struct timeval *tv) /* OUT */ +bson_iter_timeval(const bson_iter_t *iter, /* IN */ + struct timeval *tv) /* OUT */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_DATE_TIME) { - bson_iter_timeval_unsafe (iter, tv); + if (ITER_TYPE(iter) == BSON_TYPE_DATE_TIME) { + bson_iter_timeval_unsafe(iter, tv); return; } - memset (tv, 0, sizeof *tv); + memset(tv, 0, sizeof *tv); } @@ -1782,20 +1904,19 @@ bson_iter_timeval (const bson_iter_t *iter, /* IN */ */ void -bson_iter_document (const bson_iter_t *iter, /* IN */ - uint32_t *document_len, /* OUT */ - const uint8_t **document) /* OUT */ +bson_iter_document(const bson_iter_t *iter, /* IN */ + uint32_t *document_len, /* OUT */ + const uint8_t **document) /* OUT */ { - BSON_ASSERT (iter); - BSON_ASSERT (document_len); - BSON_ASSERT (document); + BSON_ASSERT(iter); + BSON_ASSERT(document_len); + BSON_ASSERT(document); *document = NULL; *document_len = 0; - if (ITER_TYPE (iter) == BSON_TYPE_DOCUMENT) { - memcpy (document_len, (iter->raw + iter->d1), sizeof (*document_len)); - *document_len = BSON_UINT32_FROM_LE (*document_len); + if (ITER_TYPE(iter) == BSON_TYPE_DOCUMENT) { + *document_len = mlib_read_u32le(iter->raw + iter->d1); *document = (iter->raw + iter->d1); } } @@ -1844,80 +1965,79 @@ bson_iter_document (const bson_iter_t *iter, /* IN */ */ void -bson_iter_array (const bson_iter_t *iter, /* IN */ - uint32_t *array_len, /* OUT */ - const uint8_t **array) /* OUT */ +bson_iter_array(const bson_iter_t *iter, /* IN */ + uint32_t *array_len, /* OUT */ + const uint8_t **array) /* OUT */ { - BSON_ASSERT (iter); - BSON_ASSERT (array_len); - BSON_ASSERT (array); + BSON_ASSERT(iter); + BSON_ASSERT(array_len); + BSON_ASSERT(array); *array = NULL; *array_len = 0; - if (ITER_TYPE (iter) == BSON_TYPE_ARRAY) { - memcpy (array_len, (iter->raw + iter->d1), sizeof (*array_len)); - *array_len = BSON_UINT32_FROM_LE (*array_len); + if (ITER_TYPE(iter) == BSON_TYPE_ARRAY) { + *array_len = mlib_read_u32le(iter->raw + iter->d1); *array = (iter->raw + iter->d1); } } #define VISIT_FIELD(name) visitor->visit_##name && visitor->visit_##name -#define VISIT_AFTER VISIT_FIELD (after) -#define VISIT_BEFORE VISIT_FIELD (before) +#define VISIT_AFTER VISIT_FIELD(after) +#define VISIT_BEFORE VISIT_FIELD(before) #define VISIT_CORRUPT \ if (visitor->visit_corrupt) \ visitor->visit_corrupt -#define VISIT_DOUBLE VISIT_FIELD (double) -#define VISIT_UTF8 VISIT_FIELD (utf8) -#define VISIT_DOCUMENT VISIT_FIELD (document) -#define VISIT_ARRAY VISIT_FIELD (array) -#define VISIT_BINARY VISIT_FIELD (binary) -#define VISIT_UNDEFINED VISIT_FIELD (undefined) -#define VISIT_OID VISIT_FIELD (oid) -#define VISIT_BOOL VISIT_FIELD (bool) -#define VISIT_DATE_TIME VISIT_FIELD (date_time) -#define VISIT_NULL VISIT_FIELD (null) -#define VISIT_REGEX VISIT_FIELD (regex) -#define VISIT_DBPOINTER VISIT_FIELD (dbpointer) -#define VISIT_CODE VISIT_FIELD (code) -#define VISIT_SYMBOL VISIT_FIELD (symbol) -#define VISIT_CODEWSCOPE VISIT_FIELD (codewscope) -#define VISIT_INT32 VISIT_FIELD (int32) -#define VISIT_TIMESTAMP VISIT_FIELD (timestamp) -#define VISIT_INT64 VISIT_FIELD (int64) -#define VISIT_DECIMAL128 VISIT_FIELD (decimal128) -#define VISIT_MAXKEY VISIT_FIELD (maxkey) -#define VISIT_MINKEY VISIT_FIELD (minkey) +#define VISIT_DOUBLE VISIT_FIELD(double) +#define VISIT_UTF8 VISIT_FIELD(utf8) +#define VISIT_DOCUMENT VISIT_FIELD(document) +#define VISIT_ARRAY VISIT_FIELD(array) +#define VISIT_BINARY VISIT_FIELD(binary) +#define VISIT_UNDEFINED VISIT_FIELD(undefined) +#define VISIT_OID VISIT_FIELD(oid) +#define VISIT_BOOL VISIT_FIELD(bool) +#define VISIT_DATE_TIME VISIT_FIELD(date_time) +#define VISIT_NULL VISIT_FIELD(null) +#define VISIT_REGEX VISIT_FIELD(regex) +#define VISIT_DBPOINTER VISIT_FIELD(dbpointer) +#define VISIT_CODE VISIT_FIELD(code) +#define VISIT_SYMBOL VISIT_FIELD(symbol) +#define VISIT_CODEWSCOPE VISIT_FIELD(codewscope) +#define VISIT_INT32 VISIT_FIELD(int32) +#define VISIT_TIMESTAMP VISIT_FIELD(timestamp) +#define VISIT_INT64 VISIT_FIELD(int64) +#define VISIT_DECIMAL128 VISIT_FIELD(decimal128) +#define VISIT_MAXKEY VISIT_FIELD(maxkey) +#define VISIT_MINKEY VISIT_FIELD(minkey) bool -bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ - const bson_visitor_t *visitor, /* IN */ - void *data) /* IN */ +bson_iter_visit_all(bson_iter_t *iter, /* INOUT */ + const bson_visitor_t *visitor, /* IN */ + void *data) /* IN */ { uint32_t bson_type = 0; const char *key = NULL; bool unsupported; - BSON_ASSERT (iter); - BSON_ASSERT (visitor); + BSON_ASSERT(iter); + BSON_ASSERT(visitor); - while (_bson_iter_next_internal (iter, 0, &key, &bson_type, &unsupported)) { - if (*key && !bson_utf8_validate (key, strlen (key), false)) { + while (_bson_iter_next_internal(iter, 0, &key, &bson_type, &unsupported)) { + if (*key && !bson_utf8_validate(key, strlen(key), false)) { iter->err_off = iter->off; break; } - if (VISIT_BEFORE (iter, key, data)) { + if (VISIT_BEFORE(iter, key, data)) { return true; } switch (bson_type) { case BSON_TYPE_DOUBLE: - if (VISIT_DOUBLE (iter, key, bson_iter_double (iter), data)) { + if (VISIT_DOUBLE(iter, key, bson_iter_double(iter), data)) { return true; } @@ -1926,14 +2046,14 @@ bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ uint32_t utf8_len; const char *utf8; - utf8 = bson_iter_utf8 (iter, &utf8_len); + utf8 = bson_iter_utf8(iter, &utf8_len); - if (!bson_utf8_validate (utf8, utf8_len, true)) { + if (!bson_utf8_validate(utf8, utf8_len, true)) { iter->err_off = iter->off; return true; } - if (VISIT_UTF8 (iter, key, utf8_len, utf8, data)) { + if (VISIT_UTF8(iter, key, utf8_len, utf8, data)) { return true; } } break; @@ -1942,13 +2062,13 @@ bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ uint32_t doclen = 0; bson_t b; - bson_iter_document (iter, &doclen, &docbuf); + bson_iter_document(iter, &doclen, &docbuf); - if (!bson_init_static (&b, docbuf, doclen)) { + if (!bson_init_static(&b, docbuf, doclen)) { iter->err_off = iter->off; break; } - if (VISIT_DOCUMENT (iter, key, &b, data)) { + if (VISIT_DOCUMENT(iter, key, &b, data)) { return true; } } break; @@ -1957,13 +2077,13 @@ bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ uint32_t doclen = 0; bson_t b; - bson_iter_array (iter, &doclen, &docbuf); + bson_iter_array(iter, &doclen, &docbuf); - if (!bson_init_static (&b, docbuf, doclen)) { + if (!bson_init_static(&b, docbuf, doclen)) { iter->err_off = iter->off; break; } - if (VISIT_ARRAY (iter, key, &b, data)) { + if (VISIT_ARRAY(iter, key, &b, data)) { return true; } } break; @@ -1972,43 +2092,43 @@ bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ bson_subtype_t subtype = BSON_SUBTYPE_BINARY; uint32_t binary_len = 0; - bson_iter_binary (iter, &subtype, &binary_len, &binary); + bson_iter_binary(iter, &subtype, &binary_len, &binary); - if (VISIT_BINARY (iter, key, subtype, binary_len, binary, data)) { + if (VISIT_BINARY(iter, key, subtype, binary_len, binary, data)) { return true; } } break; case BSON_TYPE_UNDEFINED: - if (VISIT_UNDEFINED (iter, key, data)) { + if (VISIT_UNDEFINED(iter, key, data)) { return true; } break; case BSON_TYPE_OID: - if (VISIT_OID (iter, key, bson_iter_oid (iter), data)) { + if (VISIT_OID(iter, key, bson_iter_oid(iter), data)) { return true; } break; case BSON_TYPE_BOOL: - if (VISIT_BOOL (iter, key, bson_iter_bool (iter), data)) { + if (VISIT_BOOL(iter, key, bson_iter_bool(iter), data)) { return true; } break; case BSON_TYPE_DATE_TIME: - if (VISIT_DATE_TIME (iter, key, bson_iter_date_time (iter), data)) { + if (VISIT_DATE_TIME(iter, key, bson_iter_date_time(iter), data)) { return true; } break; case BSON_TYPE_NULL: - if (VISIT_NULL (iter, key, data)) { + if (VISIT_NULL(iter, key, data)) { return true; } @@ -2016,14 +2136,14 @@ bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ case BSON_TYPE_REGEX: { const char *regex = NULL; const char *options = NULL; - regex = bson_iter_regex (iter, &options); + regex = bson_iter_regex(iter, &options); - if (!bson_utf8_validate (regex, strlen (regex), true)) { + if (!bson_utf8_validate(regex, strlen(regex), true)) { iter->err_off = iter->off; return true; } - if (VISIT_REGEX (iter, key, regex, options, data)) { + if (VISIT_REGEX(iter, key, regex, options, data)) { return true; } } break; @@ -2032,14 +2152,14 @@ bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ const char *collection = NULL; const bson_oid_t *oid = NULL; - bson_iter_dbpointer (iter, &collection_len, &collection, &oid); + bson_iter_dbpointer(iter, &collection_len, &collection, &oid); - if (!bson_utf8_validate (collection, collection_len, true)) { + if (!bson_utf8_validate(collection, collection_len, true)) { iter->err_off = iter->off; return true; } - if (VISIT_DBPOINTER (iter, key, collection_len, collection, oid, data)) { + if (VISIT_DBPOINTER(iter, key, collection_len, collection, oid, data)) { return true; } } break; @@ -2047,14 +2167,14 @@ bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ uint32_t code_len; const char *code; - code = bson_iter_code (iter, &code_len); + code = bson_iter_code(iter, &code_len); - if (!bson_utf8_validate (code, code_len, true)) { + if (!bson_utf8_validate(code, code_len, true)) { iter->err_off = iter->off; return true; } - if (VISIT_CODE (iter, key, code_len, code, data)) { + if (VISIT_CODE(iter, key, code_len, code, data)) { return true; } } break; @@ -2062,14 +2182,14 @@ bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ uint32_t symbol_len; const char *symbol; - symbol = bson_iter_symbol (iter, &symbol_len); + symbol = bson_iter_symbol(iter, &symbol_len); - if (!bson_utf8_validate (symbol, symbol_len, true)) { + if (!bson_utf8_validate(symbol, symbol_len, true)) { iter->err_off = iter->off; return true; } - if (VISIT_SYMBOL (iter, key, symbol_len, symbol, data)) { + if (VISIT_SYMBOL(iter, key, symbol_len, symbol, data)) { return true; } } break; @@ -2080,24 +2200,24 @@ bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ uint32_t doclen = 0; bson_t b; - code = bson_iter_codewscope (iter, &length, &doclen, &docbuf); + code = bson_iter_codewscope(iter, &length, &doclen, &docbuf); - if (!bson_utf8_validate (code, length, true)) { + if (!bson_utf8_validate(code, length, true)) { iter->err_off = iter->off; return true; } - if (!bson_init_static (&b, docbuf, doclen)) { + if (!bson_init_static(&b, docbuf, doclen)) { iter->err_off = iter->off; break; } - if (VISIT_CODEWSCOPE (iter, key, length, code, &b, data)) { + if (VISIT_CODEWSCOPE(iter, key, length, code, &b, data)) { return true; } } break; case BSON_TYPE_INT32: - if (VISIT_INT32 (iter, key, bson_iter_int32 (iter), data)) { + if (VISIT_INT32(iter, key, bson_iter_int32(iter), data)) { return true; } @@ -2105,37 +2225,37 @@ bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ case BSON_TYPE_TIMESTAMP: { uint32_t timestamp; uint32_t increment; - bson_iter_timestamp (iter, ×tamp, &increment); + bson_iter_timestamp(iter, ×tamp, &increment); - if (VISIT_TIMESTAMP (iter, key, timestamp, increment, data)) { + if (VISIT_TIMESTAMP(iter, key, timestamp, increment, data)) { return true; } } break; case BSON_TYPE_INT64: - if (VISIT_INT64 (iter, key, bson_iter_int64 (iter), data)) { + if (VISIT_INT64(iter, key, bson_iter_int64(iter), data)) { return true; } break; case BSON_TYPE_DECIMAL128: { bson_decimal128_t dec; - bson_iter_decimal128 (iter, &dec); + bson_iter_decimal128(iter, &dec); - if (VISIT_DECIMAL128 (iter, key, &dec, data)) { + if (VISIT_DECIMAL128(iter, key, &dec, data)) { return true; } } break; case BSON_TYPE_MAXKEY: - if (VISIT_MAXKEY (iter, bson_iter_key_unsafe (iter), data)) { + if (VISIT_MAXKEY(iter, bson_iter_key_unsafe(iter), data)) { return true; } break; case BSON_TYPE_MINKEY: - if (VISIT_MINKEY (iter, bson_iter_key_unsafe (iter), data)) { + if (VISIT_MINKEY(iter, bson_iter_key_unsafe(iter), data)) { return true; } @@ -2145,18 +2265,18 @@ bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ break; } - if (VISIT_AFTER (iter, bson_iter_key_unsafe (iter), data)) { + if (VISIT_AFTER(iter, bson_iter_key_unsafe(iter), data)) { return true; } } if (iter->err_off) { - if (unsupported && visitor->visit_unsupported_type && bson_utf8_validate (key, strlen (key), false)) { - visitor->visit_unsupported_type (iter, key, bson_type, data); + if (unsupported && visitor->visit_unsupported_type && bson_utf8_validate(key, strlen(key), false)) { + visitor->visit_unsupported_type(iter, key, bson_type, data); return false; } - VISIT_CORRUPT (iter, data); + VISIT_CORRUPT(iter, data); } #undef VISIT_FIELD @@ -2183,50 +2303,47 @@ bson_iter_visit_all (bson_iter_t *iter, /* INOUT */ */ void -bson_iter_overwrite_bool (bson_iter_t *iter, /* IN */ - bool value) /* IN */ +bson_iter_overwrite_bool(bson_iter_t *iter, /* IN */ + bool value) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_BOOL) { - memcpy ((void *) (iter->raw + iter->d1), &value, 1); + if (ITER_TYPE(iter) == BSON_TYPE_BOOL) { + memcpy((void *)(iter->raw + iter->d1), &value, 1); } } void -bson_iter_overwrite_oid (bson_iter_t *iter, const bson_oid_t *value) +bson_iter_overwrite_oid(bson_iter_t *iter, const bson_oid_t *value) { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_OID) { - memcpy ((void *) (iter->raw + iter->d1), value->bytes, sizeof (value->bytes)); + if (ITER_TYPE(iter) == BSON_TYPE_OID) { + memcpy((void *)(iter->raw + iter->d1), value->bytes, sizeof(value->bytes)); } } void -bson_iter_overwrite_timestamp (bson_iter_t *iter, uint32_t timestamp, uint32_t increment) +bson_iter_overwrite_timestamp(bson_iter_t *iter, uint32_t timestamp, uint32_t increment) { - uint64_t value; - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_TIMESTAMP) { - value = ((((uint64_t) timestamp) << 32U) | ((uint64_t) increment)); - value = BSON_UINT64_TO_LE (value); - memcpy ((void *) (iter->raw + iter->d1), &value, sizeof (value)); + if (ITER_TYPE(iter) == BSON_TYPE_TIMESTAMP) { + void *out = mlib_write_u32le((char *)iter->raw + iter->d1, increment); + mlib_write_u32le(out, timestamp); } } void -bson_iter_overwrite_date_time (bson_iter_t *iter, int64_t value) +bson_iter_overwrite_date_time(bson_iter_t *iter, int64_t value) { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_DATE_TIME) { - value = BSON_UINT64_TO_LE (value); - memcpy ((void *) (iter->raw + iter->d1), &value, sizeof (value)); + if (ITER_TYPE(iter) == BSON_TYPE_DATE_TIME) { + mlib_write_i64le((char *)iter->raw + iter->d1, value); } } @@ -2249,16 +2366,13 @@ bson_iter_overwrite_date_time (bson_iter_t *iter, int64_t value) */ void -bson_iter_overwrite_int32 (bson_iter_t *iter, /* IN */ - int32_t value) /* IN */ +bson_iter_overwrite_int32(bson_iter_t *iter, /* IN */ + int32_t value) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_INT32) { -#if BSON_BYTE_ORDER != BSON_LITTLE_ENDIAN - value = BSON_UINT32_TO_LE (value); -#endif - memcpy ((void *) (iter->raw + iter->d1), &value, sizeof (value)); + if (ITER_TYPE(iter) == BSON_TYPE_INT32) { + mlib_write_i32le((char *)iter->raw + iter->d1, value); } } @@ -2281,16 +2395,13 @@ bson_iter_overwrite_int32 (bson_iter_t *iter, /* IN */ */ void -bson_iter_overwrite_int64 (bson_iter_t *iter, /* IN */ - int64_t value) /* IN */ +bson_iter_overwrite_int64(bson_iter_t *iter, /* IN */ + int64_t value) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_INT64) { -#if BSON_BYTE_ORDER != BSON_LITTLE_ENDIAN - value = BSON_UINT64_TO_LE (value); -#endif - memcpy ((void *) (iter->raw + iter->d1), &value, sizeof (value)); + if (ITER_TYPE(iter) == BSON_TYPE_INT64) { + mlib_write_i64le((char *)iter->raw + iter->d1, value); } } @@ -2313,14 +2424,13 @@ bson_iter_overwrite_int64 (bson_iter_t *iter, /* IN */ */ void -bson_iter_overwrite_double (bson_iter_t *iter, /* IN */ - double value) /* IN */ +bson_iter_overwrite_double(bson_iter_t *iter, /* IN */ + double value) /* IN */ { - BSON_ASSERT (iter); + BSON_ASSERT(iter); - if (ITER_TYPE (iter) == BSON_TYPE_DOUBLE) { - value = BSON_DOUBLE_TO_LE (value); - memcpy ((void *) (iter->raw + iter->d1), &value, sizeof (value)); + if (ITER_TYPE(iter) == BSON_TYPE_DOUBLE) { + mlib_write_f64le((char *)iter->raw + iter->d1, value); } } @@ -2342,20 +2452,16 @@ bson_iter_overwrite_double (bson_iter_t *iter, /* IN */ *-------------------------------------------------------------------------- */ void -bson_iter_overwrite_decimal128 (bson_iter_t *iter, /* IN */ - const bson_decimal128_t *value) /* IN */ +bson_iter_overwrite_decimal128(bson_iter_t *iter, /* IN */ + const bson_decimal128_t *value) /* IN */ { - BSON_ASSERT (iter); - - if (ITER_TYPE (iter) == BSON_TYPE_DECIMAL128) { -#if BSON_BYTE_ORDER != BSON_LITTLE_ENDIAN - uint64_t data[2]; - data[0] = BSON_UINT64_TO_LE (value->low); - data[1] = BSON_UINT64_TO_LE (value->high); - memcpy ((void *) (iter->raw + iter->d1), data, sizeof (data)); -#else - memcpy ((void *) (iter->raw + iter->d1), value, sizeof (*value)); -#endif + BSON_ASSERT(iter); + + if (ITER_TYPE(iter) == BSON_TYPE_DECIMAL128) { + // low bits + void *out = mlib_write_u64le((char *)iter->raw + iter->d1, value->low); + // Followed by high bits + mlib_write_u64le(out, value->high); } } @@ -2380,78 +2486,78 @@ bson_iter_overwrite_decimal128 (bson_iter_t *iter, /* IN */ */ const bson_value_t * -bson_iter_value (bson_iter_t *iter) /* IN */ +bson_iter_value(bson_iter_t *iter) /* IN */ { bson_value_t *value; - BSON_ASSERT (iter); + BSON_ASSERT(iter); value = &iter->value; - value->value_type = ITER_TYPE (iter); + value->value_type = ITER_TYPE(iter); switch (value->value_type) { case BSON_TYPE_DOUBLE: - value->value.v_double = bson_iter_double (iter); + value->value.v_double = bson_iter_double(iter); break; case BSON_TYPE_UTF8: - value->value.v_utf8.str = (char *) bson_iter_utf8 (iter, &value->value.v_utf8.len); + value->value.v_utf8.str = (char *)bson_iter_utf8(iter, &value->value.v_utf8.len); break; case BSON_TYPE_DOCUMENT: - bson_iter_document (iter, &value->value.v_doc.data_len, (const uint8_t **) &value->value.v_doc.data); + bson_iter_document(iter, &value->value.v_doc.data_len, (const uint8_t **)&value->value.v_doc.data); break; case BSON_TYPE_ARRAY: - bson_iter_array (iter, &value->value.v_doc.data_len, (const uint8_t **) &value->value.v_doc.data); + bson_iter_array(iter, &value->value.v_doc.data_len, (const uint8_t **)&value->value.v_doc.data); break; case BSON_TYPE_BINARY: - bson_iter_binary (iter, - &value->value.v_binary.subtype, - &value->value.v_binary.data_len, - (const uint8_t **) &value->value.v_binary.data); + bson_iter_binary(iter, + &value->value.v_binary.subtype, + &value->value.v_binary.data_len, + (const uint8_t **)&value->value.v_binary.data); break; case BSON_TYPE_OID: - bson_oid_copy (bson_iter_oid (iter), &value->value.v_oid); + bson_oid_copy(bson_iter_oid(iter), &value->value.v_oid); break; case BSON_TYPE_BOOL: - value->value.v_bool = bson_iter_bool (iter); + value->value.v_bool = bson_iter_bool(iter); break; case BSON_TYPE_DATE_TIME: - value->value.v_datetime = bson_iter_date_time (iter); + value->value.v_datetime = bson_iter_date_time(iter); break; case BSON_TYPE_REGEX: - value->value.v_regex.regex = (char *) bson_iter_regex (iter, (const char **) &value->value.v_regex.options); + value->value.v_regex.regex = (char *)bson_iter_regex(iter, (const char **)&value->value.v_regex.options); break; case BSON_TYPE_DBPOINTER: { const bson_oid_t *oid; - bson_iter_dbpointer ( - iter, &value->value.v_dbpointer.collection_len, (const char **) &value->value.v_dbpointer.collection, &oid); - bson_oid_copy (oid, &value->value.v_dbpointer.oid); + bson_iter_dbpointer( + iter, &value->value.v_dbpointer.collection_len, (const char **)&value->value.v_dbpointer.collection, &oid); + bson_oid_copy(oid, &value->value.v_dbpointer.oid); break; } case BSON_TYPE_CODE: - value->value.v_code.code = (char *) bson_iter_code (iter, &value->value.v_code.code_len); + value->value.v_code.code = (char *)bson_iter_code(iter, &value->value.v_code.code_len); break; case BSON_TYPE_SYMBOL: - value->value.v_symbol.symbol = (char *) bson_iter_symbol (iter, &value->value.v_symbol.len); + value->value.v_symbol.symbol = (char *)bson_iter_symbol(iter, &value->value.v_symbol.len); break; case BSON_TYPE_CODEWSCOPE: value->value.v_codewscope.code = - (char *) bson_iter_codewscope (iter, - &value->value.v_codewscope.code_len, - &value->value.v_codewscope.scope_len, - (const uint8_t **) &value->value.v_codewscope.scope_data); + (char *)bson_iter_codewscope(iter, + &value->value.v_codewscope.code_len, + &value->value.v_codewscope.scope_len, + (const uint8_t **)&value->value.v_codewscope.scope_data); break; case BSON_TYPE_INT32: - value->value.v_int32 = bson_iter_int32 (iter); + value->value.v_int32 = bson_iter_int32(iter); break; case BSON_TYPE_TIMESTAMP: - bson_iter_timestamp (iter, &value->value.v_timestamp.timestamp, &value->value.v_timestamp.increment); + bson_iter_timestamp(iter, &value->value.v_timestamp.timestamp, &value->value.v_timestamp.increment); break; case BSON_TYPE_INT64: - value->value.v_int64 = bson_iter_int64 (iter); + value->value.v_int64 = bson_iter_int64(iter); break; case BSON_TYPE_DECIMAL128: - bson_iter_decimal128 (iter, &(value->value.v_decimal128)); + bson_iter_decimal128(iter, &(value->value.v_decimal128)); break; case BSON_TYPE_NULL: case BSON_TYPE_UNDEFINED: @@ -2467,7 +2573,7 @@ bson_iter_value (bson_iter_t *iter) /* IN */ } uint32_t -bson_iter_key_len (const bson_iter_t *iter) +bson_iter_key_len(const bson_iter_t *iter) { /* * f i e l d n a m e \0 _ @@ -2476,36 +2582,36 @@ bson_iter_key_len (const bson_iter_t *iter) * iter->key iter->d1 * */ - BSON_ASSERT (iter->d1 > iter->key); + BSON_ASSERT(iter->d1 > iter->key); return iter->d1 - iter->key - 1; } bool -bson_iter_init_from_data_at_offset ( +bson_iter_init_from_data_at_offset( bson_iter_t *iter, const uint8_t *data, size_t length, uint32_t offset, uint32_t keylen) { const char *key; uint32_t bson_type; bool unsupported; - BSON_ASSERT (iter); - BSON_ASSERT (data); + BSON_ASSERT(iter); + BSON_ASSERT(data); - if (BSON_UNLIKELY ((length < 5) || (length > INT_MAX))) { - memset (iter, 0, sizeof *iter); + if (BSON_UNLIKELY((length < 5) || (length > INT_MAX))) { + memset(iter, 0, sizeof *iter); return false; } - iter->raw = (uint8_t *) data; - iter->len = (uint32_t) length; + iter->raw = (uint8_t *)data; + iter->len = (uint32_t)length; iter->off = 0; iter->type = 0; iter->key = 0; iter->next_off = offset; iter->err_off = 0; - if (!_bson_iter_next_internal (iter, keylen, &key, &bson_type, &unsupported)) { - memset (iter, 0, sizeof *iter); + if (!_bson_iter_next_internal(iter, keylen, &key, &bson_type, &unsupported)) { + memset(iter, 0, sizeof *iter); return false; } @@ -2513,7 +2619,7 @@ bson_iter_init_from_data_at_offset ( } uint32_t -bson_iter_offset (bson_iter_t *iter) +bson_iter_offset(bson_iter_t *iter) { return iter->off; } diff --git a/bsonjs/bson/bson-iter.h b/bsonjs/bson/bson-iter.h index 5370922..1f7e255 100644 --- a/bsonjs/bson/bson-iter.h +++ b/bsonjs/bson/bson-iter.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,66 +21,75 @@ #define BSON_ITER_H -#include "bson.h" #include -#include #include +#include +#include BSON_BEGIN_DECLS -#define BSON_ITER_HOLDS_DOUBLE(iter) (bson_iter_type ((iter)) == BSON_TYPE_DOUBLE) +#define BSON_ITER_HOLDS_DOUBLE(iter) (bson_iter_type((iter)) == BSON_TYPE_DOUBLE) -#define BSON_ITER_HOLDS_UTF8(iter) (bson_iter_type ((iter)) == BSON_TYPE_UTF8) +#define BSON_ITER_HOLDS_UTF8(iter) (bson_iter_type((iter)) == BSON_TYPE_UTF8) -#define BSON_ITER_HOLDS_DOCUMENT(iter) (bson_iter_type ((iter)) == BSON_TYPE_DOCUMENT) +#define BSON_ITER_HOLDS_DOCUMENT(iter) (bson_iter_type((iter)) == BSON_TYPE_DOCUMENT) -#define BSON_ITER_HOLDS_ARRAY(iter) (bson_iter_type ((iter)) == BSON_TYPE_ARRAY) +#define BSON_ITER_HOLDS_ARRAY(iter) (bson_iter_type((iter)) == BSON_TYPE_ARRAY) -#define BSON_ITER_HOLDS_BINARY(iter) (bson_iter_type ((iter)) == BSON_TYPE_BINARY) +#define BSON_ITER_HOLDS_BINARY(iter) (bson_iter_type((iter)) == BSON_TYPE_BINARY) -#define BSON_ITER_HOLDS_UNDEFINED(iter) (bson_iter_type ((iter)) == BSON_TYPE_UNDEFINED) +#define BSON_ITER_HOLDS_VECTOR(iter) \ + (BSON_ITER_HOLDS_BINARY(iter) && bson_iter_binary_subtype(iter) == BSON_SUBTYPE_VECTOR) -#define BSON_ITER_HOLDS_OID(iter) (bson_iter_type ((iter)) == BSON_TYPE_OID) +#define BSON_ITER_HOLDS_VECTOR_INT8(iter) (bson_vector_int8_const_view_from_iter(NULL, iter)) -#define BSON_ITER_HOLDS_BOOL(iter) (bson_iter_type ((iter)) == BSON_TYPE_BOOL) +#define BSON_ITER_HOLDS_VECTOR_FLOAT32(iter) (bson_vector_float32_const_view_from_iter(NULL, iter)) -#define BSON_ITER_HOLDS_DATE_TIME(iter) (bson_iter_type ((iter)) == BSON_TYPE_DATE_TIME) +#define BSON_ITER_HOLDS_VECTOR_PACKED_BIT(iter) (bson_vector_packed_bit_const_view_from_iter(NULL, iter)) -#define BSON_ITER_HOLDS_NULL(iter) (bson_iter_type ((iter)) == BSON_TYPE_NULL) +#define BSON_ITER_HOLDS_UNDEFINED(iter) (bson_iter_type((iter)) == BSON_TYPE_UNDEFINED) -#define BSON_ITER_HOLDS_REGEX(iter) (bson_iter_type ((iter)) == BSON_TYPE_REGEX) +#define BSON_ITER_HOLDS_OID(iter) (bson_iter_type((iter)) == BSON_TYPE_OID) -#define BSON_ITER_HOLDS_DBPOINTER(iter) (bson_iter_type ((iter)) == BSON_TYPE_DBPOINTER) +#define BSON_ITER_HOLDS_BOOL(iter) (bson_iter_type((iter)) == BSON_TYPE_BOOL) -#define BSON_ITER_HOLDS_CODE(iter) (bson_iter_type ((iter)) == BSON_TYPE_CODE) +#define BSON_ITER_HOLDS_DATE_TIME(iter) (bson_iter_type((iter)) == BSON_TYPE_DATE_TIME) -#define BSON_ITER_HOLDS_SYMBOL(iter) (bson_iter_type ((iter)) == BSON_TYPE_SYMBOL) +#define BSON_ITER_HOLDS_NULL(iter) (bson_iter_type((iter)) == BSON_TYPE_NULL) -#define BSON_ITER_HOLDS_CODEWSCOPE(iter) (bson_iter_type ((iter)) == BSON_TYPE_CODEWSCOPE) +#define BSON_ITER_HOLDS_REGEX(iter) (bson_iter_type((iter)) == BSON_TYPE_REGEX) -#define BSON_ITER_HOLDS_INT32(iter) (bson_iter_type ((iter)) == BSON_TYPE_INT32) +#define BSON_ITER_HOLDS_DBPOINTER(iter) (bson_iter_type((iter)) == BSON_TYPE_DBPOINTER) -#define BSON_ITER_HOLDS_TIMESTAMP(iter) (bson_iter_type ((iter)) == BSON_TYPE_TIMESTAMP) +#define BSON_ITER_HOLDS_CODE(iter) (bson_iter_type((iter)) == BSON_TYPE_CODE) -#define BSON_ITER_HOLDS_INT64(iter) (bson_iter_type ((iter)) == BSON_TYPE_INT64) +#define BSON_ITER_HOLDS_SYMBOL(iter) (bson_iter_type((iter)) == BSON_TYPE_SYMBOL) -#define BSON_ITER_HOLDS_DECIMAL128(iter) (bson_iter_type ((iter)) == BSON_TYPE_DECIMAL128) +#define BSON_ITER_HOLDS_CODEWSCOPE(iter) (bson_iter_type((iter)) == BSON_TYPE_CODEWSCOPE) -#define BSON_ITER_HOLDS_MAXKEY(iter) (bson_iter_type ((iter)) == BSON_TYPE_MAXKEY) +#define BSON_ITER_HOLDS_INT32(iter) (bson_iter_type((iter)) == BSON_TYPE_INT32) -#define BSON_ITER_HOLDS_MINKEY(iter) (bson_iter_type ((iter)) == BSON_TYPE_MINKEY) +#define BSON_ITER_HOLDS_TIMESTAMP(iter) (bson_iter_type((iter)) == BSON_TYPE_TIMESTAMP) -#define BSON_ITER_HOLDS_INT(iter) (BSON_ITER_HOLDS_INT32 (iter) || BSON_ITER_HOLDS_INT64 (iter)) +#define BSON_ITER_HOLDS_INT64(iter) (bson_iter_type((iter)) == BSON_TYPE_INT64) -#define BSON_ITER_HOLDS_NUMBER(iter) (BSON_ITER_HOLDS_INT (iter) || BSON_ITER_HOLDS_DOUBLE (iter)) +#define BSON_ITER_HOLDS_DECIMAL128(iter) (bson_iter_type((iter)) == BSON_TYPE_DECIMAL128) -#define BSON_ITER_IS_KEY(iter, key) (0 == strcmp ((key), bson_iter_key ((iter)))) +#define BSON_ITER_HOLDS_MAXKEY(iter) (bson_iter_type((iter)) == BSON_TYPE_MAXKEY) +#define BSON_ITER_HOLDS_MINKEY(iter) (bson_iter_type((iter)) == BSON_TYPE_MINKEY) -BSON_EXPORT (const bson_value_t *) -bson_iter_value (bson_iter_t *iter); +#define BSON_ITER_HOLDS_INT(iter) (BSON_ITER_HOLDS_INT32(iter) || BSON_ITER_HOLDS_INT64(iter)) + +#define BSON_ITER_HOLDS_NUMBER(iter) (BSON_ITER_HOLDS_INT(iter) || BSON_ITER_HOLDS_DOUBLE(iter)) + +#define BSON_ITER_IS_KEY(iter, key) (0 == strcmp((key), bson_iter_key((iter)))) + + +BSON_EXPORT(const bson_value_t *) +bson_iter_value(bson_iter_t *iter); /** @@ -92,30 +101,41 @@ bson_iter_value (bson_iter_t *iter); * bson_validate() is one way to do this ahead of time. */ static BSON_INLINE uint32_t -bson_iter_utf8_len_unsafe (const bson_iter_t *iter) +bson_iter_utf8_len_unsafe(const bson_iter_t *iter) { uint32_t raw; - memcpy (&raw, iter->raw + iter->d1, sizeof (raw)); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + memcpy(&raw, iter->raw + iter->d1, sizeof(raw)); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - const uint32_t native = BSON_UINT32_FROM_LE (raw); + const uint32_t native = BSON_UINT32_FROM_LE(raw); int32_t len; - memcpy (&len, &native, sizeof (len)); + memcpy(&len, &native, sizeof(len)); - return len <= 0 ? 0u : (uint32_t) (len - 1); + return len <= 0 ? 0u : (uint32_t)(len - 1); } -BSON_EXPORT (void) -bson_iter_array (const bson_iter_t *iter, uint32_t *array_len, const uint8_t **array); +BSON_EXPORT(void) +bson_iter_array(const bson_iter_t *iter, uint32_t *array_len, const uint8_t **array); + + +BSON_EXPORT(void) +bson_iter_binary(const bson_iter_t *iter, bson_subtype_t *subtype, uint32_t *binary_len, const uint8_t **binary); + +BSON_EXPORT(void) +bson_iter_overwrite_binary(bson_iter_t *iter, bson_subtype_t subtype, uint32_t *binary_len, uint8_t **binary); +BSON_EXPORT(bson_subtype_t) +bson_iter_binary_subtype(const bson_iter_t *iter); -BSON_EXPORT (void) -bson_iter_binary (const bson_iter_t *iter, bson_subtype_t *subtype, uint32_t *binary_len, const uint8_t **binary); +BSON_EXPORT(bool) +bson_iter_binary_equal(const bson_iter_t *iter_a, const bson_iter_t *iter_b); -BSON_EXPORT (const char *) -bson_iter_code (const bson_iter_t *iter, uint32_t *length); +BSON_EXPORT(const char *) +bson_iter_code(const bson_iter_t *iter, uint32_t *length); /** @@ -128,33 +148,32 @@ bson_iter_code (const bson_iter_t *iter, uint32_t *length); * Returns: A string that should not be modified or freed. */ static BSON_INLINE const char * -bson_iter_code_unsafe (const bson_iter_t *iter, uint32_t *length) +bson_iter_code_unsafe(const bson_iter_t *iter, uint32_t *length) { - *length = bson_iter_utf8_len_unsafe (iter); - return (const char *) (iter->raw + iter->d2); + *length = bson_iter_utf8_len_unsafe(iter); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + return (const char *)(iter->raw + iter->d2); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END } -BSON_EXPORT (const char *) -bson_iter_codewscope (const bson_iter_t *iter, uint32_t *length, uint32_t *scope_len, const uint8_t **scope); +BSON_EXPORT(const char *) +bson_iter_codewscope(const bson_iter_t *iter, uint32_t *length, uint32_t *scope_len, const uint8_t **scope); -BSON_EXPORT (void) -bson_iter_dbpointer (const bson_iter_t *iter, - uint32_t *collection_len, - const char **collection, - const bson_oid_t **oid); +BSON_EXPORT(void) +bson_iter_dbpointer(const bson_iter_t *iter, uint32_t *collection_len, const char **collection, const bson_oid_t **oid); -BSON_EXPORT (void) -bson_iter_document (const bson_iter_t *iter, uint32_t *document_len, const uint8_t **document); +BSON_EXPORT(void) +bson_iter_document(const bson_iter_t *iter, uint32_t *document_len, const uint8_t **document); -BSON_EXPORT (double) -bson_iter_double (const bson_iter_t *iter); +BSON_EXPORT(double) +bson_iter_double(const bson_iter_t *iter); -BSON_EXPORT (double) -bson_iter_as_double (const bson_iter_t *iter); +BSON_EXPORT(double) +bson_iter_as_double(const bson_iter_t *iter); /** * bson_iter_double_unsafe: @@ -165,39 +184,41 @@ bson_iter_as_double (const bson_iter_t *iter); * Returns: A double. */ static BSON_INLINE double -bson_iter_double_unsafe (const bson_iter_t *iter) +bson_iter_double_unsafe(const bson_iter_t *iter) { double val; - memcpy (&val, iter->raw + iter->d1, sizeof (val)); - return BSON_DOUBLE_FROM_LE (val); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + memcpy(&val, iter->raw + iter->d1, sizeof(val)); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + return BSON_DOUBLE_FROM_LE(val); } -BSON_EXPORT (bool) -bson_iter_init (bson_iter_t *iter, const bson_t *bson); +BSON_EXPORT(bool) +bson_iter_init(bson_iter_t *iter, const bson_t *bson); -BSON_EXPORT (bool) -bson_iter_init_from_data (bson_iter_t *iter, const uint8_t *data, size_t length); +BSON_EXPORT(bool) +bson_iter_init_from_data(bson_iter_t *iter, const uint8_t *data, size_t length); -BSON_EXPORT (bool) -bson_iter_init_find (bson_iter_t *iter, const bson_t *bson, const char *key); +BSON_EXPORT(bool) +bson_iter_init_find(bson_iter_t *iter, const bson_t *bson, const char *key); -BSON_EXPORT (bool) -bson_iter_init_find_w_len (bson_iter_t *iter, const bson_t *bson, const char *key, int keylen); +BSON_EXPORT(bool) +bson_iter_init_find_w_len(bson_iter_t *iter, const bson_t *bson, const char *key, int keylen); -BSON_EXPORT (bool) -bson_iter_init_find_case (bson_iter_t *iter, const bson_t *bson, const char *key); +BSON_EXPORT(bool) +bson_iter_init_find_case(bson_iter_t *iter, const bson_t *bson, const char *key); -BSON_EXPORT (bool) -bson_iter_init_from_data_at_offset ( +BSON_EXPORT(bool) +bson_iter_init_from_data_at_offset( bson_iter_t *iter, const uint8_t *data, size_t length, uint32_t offset, uint32_t keylen); -BSON_EXPORT (int32_t) -bson_iter_int32 (const bson_iter_t *iter); +BSON_EXPORT(int32_t) +bson_iter_int32(const bson_iter_t *iter); /** @@ -209,25 +230,27 @@ bson_iter_int32 (const bson_iter_t *iter); * Returns: A 32-bit signed integer. */ static BSON_INLINE int32_t -bson_iter_int32_unsafe (const bson_iter_t *iter) +bson_iter_int32_unsafe(const bson_iter_t *iter) { uint32_t raw; - memcpy (&raw, iter->raw + iter->d1, sizeof (raw)); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + memcpy(&raw, iter->raw + iter->d1, sizeof(raw)); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - const uint32_t native = BSON_UINT32_FROM_LE (raw); + const uint32_t native = BSON_UINT32_FROM_LE(raw); int32_t res; - memcpy (&res, &native, sizeof (res)); + memcpy(&res, &native, sizeof(res)); return res; } -BSON_EXPORT (int64_t) -bson_iter_int64 (const bson_iter_t *iter); +BSON_EXPORT(int64_t) +bson_iter_int64(const bson_iter_t *iter); -BSON_EXPORT (int64_t) -bson_iter_as_int64 (const bson_iter_t *iter); +BSON_EXPORT(int64_t) +bson_iter_as_int64(const bson_iter_t *iter); /** @@ -239,41 +262,43 @@ bson_iter_as_int64 (const bson_iter_t *iter); * Returns: A 64-bit signed integer. */ static BSON_INLINE int64_t -bson_iter_int64_unsafe (const bson_iter_t *iter) +bson_iter_int64_unsafe(const bson_iter_t *iter) { uint64_t raw; - memcpy (&raw, iter->raw + iter->d1, sizeof (raw)); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + memcpy(&raw, iter->raw + iter->d1, sizeof(raw)); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - const uint64_t native = BSON_UINT64_FROM_LE (raw); + const uint64_t native = BSON_UINT64_FROM_LE(raw); int64_t res; - memcpy (&res, &native, sizeof (res)); + memcpy(&res, &native, sizeof(res)); return res; } -BSON_EXPORT (bool) -bson_iter_find (bson_iter_t *iter, const char *key); +BSON_EXPORT(bool) +bson_iter_find(bson_iter_t *iter, const char *key); -BSON_EXPORT (bool) -bson_iter_find_w_len (bson_iter_t *iter, const char *key, int keylen); +BSON_EXPORT(bool) +bson_iter_find_w_len(bson_iter_t *iter, const char *key, int keylen); -BSON_EXPORT (bool) -bson_iter_find_case (bson_iter_t *iter, const char *key); +BSON_EXPORT(bool) +bson_iter_find_case(bson_iter_t *iter, const char *key); -BSON_EXPORT (bool) -bson_iter_find_descendant (bson_iter_t *iter, const char *dotkey, bson_iter_t *descendant); +BSON_EXPORT(bool) +bson_iter_find_descendant(bson_iter_t *iter, const char *dotkey, bson_iter_t *descendant); -BSON_EXPORT (bool) -bson_iter_next (bson_iter_t *iter); +BSON_EXPORT(bool) +bson_iter_next(bson_iter_t *iter); -BSON_EXPORT (const bson_oid_t *) -bson_iter_oid (const bson_iter_t *iter); +BSON_EXPORT(const bson_oid_t *) +bson_iter_oid(const bson_iter_t *iter); /** @@ -285,14 +310,16 @@ bson_iter_oid (const bson_iter_t *iter); * Returns: A #bson_oid_t that should not be modified or freed. */ static BSON_INLINE const bson_oid_t * -bson_iter_oid_unsafe (const bson_iter_t *iter) +bson_iter_oid_unsafe(const bson_iter_t *iter) { - return (const bson_oid_t *) (iter->raw + iter->d1); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + return (const bson_oid_t *)(iter->raw + iter->d1); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END } -BSON_EXPORT (bool) -bson_iter_decimal128 (const bson_iter_t *iter, bson_decimal128_t *dec); +BSON_EXPORT(bool) +bson_iter_decimal128(const bson_iter_t *iter, bson_decimal128_t *dec); /** @@ -304,24 +331,26 @@ bson_iter_decimal128 (const bson_iter_t *iter, bson_decimal128_t *dec); * Returns: A #bson_decimal128_t. */ static BSON_INLINE void -bson_iter_decimal128_unsafe (const bson_iter_t *iter, bson_decimal128_t *dec) +bson_iter_decimal128_unsafe(const bson_iter_t *iter, bson_decimal128_t *dec) { uint64_t low_le; uint64_t high_le; - memcpy (&low_le, iter->raw + iter->d1, sizeof (low_le)); - memcpy (&high_le, iter->raw + iter->d1 + 8, sizeof (high_le)); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + memcpy(&low_le, iter->raw + iter->d1, sizeof(low_le)); + memcpy(&high_le, iter->raw + iter->d1 + 8, sizeof(high_le)); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - dec->low = BSON_UINT64_FROM_LE (low_le); - dec->high = BSON_UINT64_FROM_LE (high_le); + dec->low = BSON_UINT64_FROM_LE(low_le); + dec->high = BSON_UINT64_FROM_LE(high_le); } -BSON_EXPORT (const char *) -bson_iter_key (const bson_iter_t *iter); +BSON_EXPORT(const char *) +bson_iter_key(const bson_iter_t *iter); -BSON_EXPORT (uint32_t) -bson_iter_key_len (const bson_iter_t *iter); +BSON_EXPORT(uint32_t) +bson_iter_key_len(const bson_iter_t *iter); /** @@ -333,14 +362,16 @@ bson_iter_key_len (const bson_iter_t *iter); * Returns: A string that should not be modified or freed. */ static BSON_INLINE const char * -bson_iter_key_unsafe (const bson_iter_t *iter) +bson_iter_key_unsafe(const bson_iter_t *iter) { - return (const char *) (iter->raw + iter->key); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + return (const char *)(iter->raw + iter->key); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END } -BSON_EXPORT (const char *) -bson_iter_utf8 (const bson_iter_t *iter, uint32_t *length); +BSON_EXPORT(const char *) +bson_iter_utf8(const bson_iter_t *iter, uint32_t *length); /** @@ -351,23 +382,25 @@ bson_iter_utf8 (const bson_iter_t *iter, uint32_t *length); * Returns: A string that should not be modified or freed. */ static BSON_INLINE const char * -bson_iter_utf8_unsafe (const bson_iter_t *iter, size_t *length) +bson_iter_utf8_unsafe(const bson_iter_t *iter, size_t *length) { - *length = bson_iter_utf8_len_unsafe (iter); - return (const char *) (iter->raw + iter->d2); + *length = bson_iter_utf8_len_unsafe(iter); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + return (const char *)(iter->raw + iter->d2); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END } -BSON_EXPORT (char *) -bson_iter_dup_utf8 (const bson_iter_t *iter, uint32_t *length); +BSON_EXPORT(char *) +bson_iter_dup_utf8(const bson_iter_t *iter, uint32_t *length); -BSON_EXPORT (int64_t) -bson_iter_date_time (const bson_iter_t *iter); +BSON_EXPORT(int64_t) +bson_iter_date_time(const bson_iter_t *iter); -BSON_EXPORT (time_t) -bson_iter_time_t (const bson_iter_t *iter); +BSON_EXPORT(time_t) +bson_iter_time_t(const bson_iter_t *iter); /** @@ -380,14 +413,14 @@ bson_iter_time_t (const bson_iter_t *iter); * in UTC. */ static BSON_INLINE time_t -bson_iter_time_t_unsafe (const bson_iter_t *iter) +bson_iter_time_t_unsafe(const bson_iter_t *iter) { - return (time_t) (bson_iter_int64_unsafe (iter) / 1000); + return (time_t)(bson_iter_int64_unsafe(iter) / 1000); } -BSON_EXPORT (void) -bson_iter_timeval (const bson_iter_t *iter, struct timeval *tv); +BSON_EXPORT(void) +bson_iter_timeval(const bson_iter_t *iter, struct timeval *tv); /** @@ -398,25 +431,25 @@ bson_iter_timeval (const bson_iter_t *iter, struct timeval *tv); * Similar to bson_iter_timeval() but performs no integrity checking. */ static BSON_INLINE void -bson_iter_timeval_unsafe (const bson_iter_t *iter, struct timeval *tv) +bson_iter_timeval_unsafe(const bson_iter_t *iter, struct timeval *tv) { - int64_t value = bson_iter_int64_unsafe (iter); + int64_t value = bson_iter_int64_unsafe(iter); #ifdef BSON_OS_WIN32 - tv->tv_sec = (long) (value / 1000); - tv->tv_usec = (long) (value % 1000) * 1000; + tv->tv_sec = (long)(value / 1000); + tv->tv_usec = (long)(value % 1000) * 1000; #else - tv->tv_sec = (time_t) (value / 1000); - tv->tv_usec = (suseconds_t) (value % 1000) * 1000; + tv->tv_sec = (time_t)(value / 1000); + tv->tv_usec = (suseconds_t)(value % 1000) * 1000; #endif } -BSON_EXPORT (void) -bson_iter_timestamp (const bson_iter_t *iter, uint32_t *timestamp, uint32_t *increment); +BSON_EXPORT(void) +bson_iter_timestamp(const bson_iter_t *iter, uint32_t *timestamp, uint32_t *increment); -BSON_EXPORT (bool) -bson_iter_bool (const bson_iter_t *iter); +BSON_EXPORT(bool) +bson_iter_bool(const bson_iter_t *iter); /** @@ -428,29 +461,31 @@ bson_iter_bool (const bson_iter_t *iter); * Returns: true or false. */ static BSON_INLINE bool -bson_iter_bool_unsafe (const bson_iter_t *iter) +bson_iter_bool_unsafe(const bson_iter_t *iter) { char val; - memcpy (&val, iter->raw + iter->d1, 1); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + memcpy(&val, iter->raw + iter->d1, 1); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END return !!val; } -BSON_EXPORT (bool) -bson_iter_as_bool (const bson_iter_t *iter); +BSON_EXPORT(bool) +bson_iter_as_bool(const bson_iter_t *iter); -BSON_EXPORT (const char *) -bson_iter_regex (const bson_iter_t *iter, const char **options); +BSON_EXPORT(const char *) +bson_iter_regex(const bson_iter_t *iter, const char **options); -BSON_EXPORT (const char *) -bson_iter_symbol (const bson_iter_t *iter, uint32_t *length); +BSON_EXPORT(const char *) +bson_iter_symbol(const bson_iter_t *iter, uint32_t *length); -BSON_EXPORT (bson_type_t) -bson_iter_type (const bson_iter_t *iter); +BSON_EXPORT(bson_type_t) +bson_iter_type(const bson_iter_t *iter); /** @@ -462,53 +497,55 @@ bson_iter_type (const bson_iter_t *iter); * Returns: A bson_type_t. */ static BSON_INLINE bson_type_t -bson_iter_type_unsafe (const bson_iter_t *iter) +bson_iter_type_unsafe(const bson_iter_t *iter) { - return (bson_type_t) (iter->raw + iter->type)[0]; + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + return (bson_type_t)(iter->raw + iter->type)[0]; + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END } -BSON_EXPORT (bool) -bson_iter_recurse (const bson_iter_t *iter, bson_iter_t *child); +BSON_EXPORT(bool) +bson_iter_recurse(const bson_iter_t *iter, bson_iter_t *child); -BSON_EXPORT (void) -bson_iter_overwrite_int32 (bson_iter_t *iter, int32_t value); +BSON_EXPORT(void) +bson_iter_overwrite_int32(bson_iter_t *iter, int32_t value); -BSON_EXPORT (void) -bson_iter_overwrite_int64 (bson_iter_t *iter, int64_t value); +BSON_EXPORT(void) +bson_iter_overwrite_int64(bson_iter_t *iter, int64_t value); -BSON_EXPORT (void) -bson_iter_overwrite_double (bson_iter_t *iter, double value); +BSON_EXPORT(void) +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_EXPORT(void) +bson_iter_overwrite_decimal128(bson_iter_t *iter, const bson_decimal128_t *value); -BSON_EXPORT (void) -bson_iter_overwrite_bool (bson_iter_t *iter, bool value); +BSON_EXPORT(void) +bson_iter_overwrite_bool(bson_iter_t *iter, bool value); -BSON_EXPORT (void) -bson_iter_overwrite_oid (bson_iter_t *iter, const bson_oid_t *value); +BSON_EXPORT(void) +bson_iter_overwrite_oid(bson_iter_t *iter, const bson_oid_t *value); -BSON_EXPORT (void) -bson_iter_overwrite_timestamp (bson_iter_t *iter, uint32_t timestamp, uint32_t increment); +BSON_EXPORT(void) +bson_iter_overwrite_timestamp(bson_iter_t *iter, uint32_t timestamp, uint32_t increment); -BSON_EXPORT (void) -bson_iter_overwrite_date_time (bson_iter_t *iter, int64_t value); +BSON_EXPORT(void) +bson_iter_overwrite_date_time(bson_iter_t *iter, int64_t value); -BSON_EXPORT (bool) -bson_iter_visit_all (bson_iter_t *iter, const bson_visitor_t *visitor, void *data); +BSON_EXPORT(bool) +bson_iter_visit_all(bson_iter_t *iter, const bson_visitor_t *visitor, void *data); -BSON_EXPORT (uint32_t) -bson_iter_offset (bson_iter_t *iter); +BSON_EXPORT(uint32_t) +bson_iter_offset(bson_iter_t *iter); BSON_END_DECLS diff --git a/bsonjs/bson/bson-json-private.h b/bsonjs/bson/bson-json-private.h index 7a562a2..4e9dc73 100644 --- a/bsonjs/bson/bson-json-private.h +++ b/bsonjs/bson/bson-json-private.h @@ -1,5 +1,5 @@ /* - * Copyright 2020 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ #ifndef BSON_JSON_PRIVATE_H #define BSON_JSON_PRIVATE_H +#include // IWYU pragma: export struct _bson_json_opts_t { bson_json_mode_t mode; diff --git a/bsonjs/bson/bson-json.c b/bsonjs/bson/bson-json.c index a2e3537..5bf64f5 100644 --- a/bsonjs/bson/bson-json.c +++ b/bsonjs/bson/bson-json.c @@ -1,5 +1,5 @@ /* - * Copyright 2014 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,19 +15,26 @@ */ -#include -#include -#include -#include - -#include "bson.h" -#include #include -#include + +#include #include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include -#include "common-b64-private.h" -#include "jsonsl/jsonsl.h" +#include +#include #ifdef _WIN32 #include @@ -52,62 +59,62 @@ #define READ_STATE_ENUM(ENUM) BSON_JSON_##ENUM, #define GENERATE_STRING(STRING) #STRING, -#define FOREACH_READ_STATE(RS) \ - RS (REGULAR) \ - RS (DONE) \ - RS (ERROR) \ - RS (IN_START_MAP) \ - RS (IN_BSON_TYPE) \ - RS (IN_BSON_TYPE_DATE_NUMBERLONG) \ - RS (IN_BSON_TYPE_DATE_ENDMAP) \ - RS (IN_BSON_TYPE_TIMESTAMP_STARTMAP) \ - RS (IN_BSON_TYPE_TIMESTAMP_VALUES) \ - RS (IN_BSON_TYPE_TIMESTAMP_ENDMAP) \ - RS (IN_BSON_TYPE_REGEX_STARTMAP) \ - RS (IN_BSON_TYPE_REGEX_VALUES) \ - RS (IN_BSON_TYPE_REGEX_ENDMAP) \ - RS (IN_BSON_TYPE_BINARY_VALUES) \ - RS (IN_BSON_TYPE_BINARY_ENDMAP) \ - RS (IN_BSON_TYPE_SCOPE_STARTMAP) \ - RS (IN_BSON_TYPE_DBPOINTER_STARTMAP) \ - RS (IN_SCOPE) \ - RS (IN_DBPOINTER) - -typedef enum { FOREACH_READ_STATE (READ_STATE_ENUM) } bson_json_read_state_t; - -static const char *read_state_names[] = {FOREACH_READ_STATE (GENERATE_STRING)}; +#define FOREACH_READ_STATE(RS) \ + RS(REGULAR) \ + RS(DONE) \ + RS(ERROR) \ + RS(IN_START_MAP) \ + RS(IN_BSON_TYPE) \ + RS(IN_BSON_TYPE_DATE_NUMBERLONG) \ + RS(IN_BSON_TYPE_DATE_ENDMAP) \ + RS(IN_BSON_TYPE_TIMESTAMP_STARTMAP) \ + RS(IN_BSON_TYPE_TIMESTAMP_VALUES) \ + RS(IN_BSON_TYPE_TIMESTAMP_ENDMAP) \ + RS(IN_BSON_TYPE_REGEX_STARTMAP) \ + RS(IN_BSON_TYPE_REGEX_VALUES) \ + RS(IN_BSON_TYPE_REGEX_ENDMAP) \ + RS(IN_BSON_TYPE_BINARY_VALUES) \ + RS(IN_BSON_TYPE_BINARY_ENDMAP) \ + RS(IN_BSON_TYPE_SCOPE_STARTMAP) \ + RS(IN_BSON_TYPE_DBPOINTER_STARTMAP) \ + RS(IN_SCOPE) \ + RS(IN_DBPOINTER) + +typedef enum { FOREACH_READ_STATE(READ_STATE_ENUM) } bson_json_read_state_t; + +static const char *read_state_names[] = {FOREACH_READ_STATE(GENERATE_STRING)}; #define BSON_STATE_ENUM(ENUM) BSON_JSON_LF_##ENUM, #define FOREACH_BSON_STATE(BS) \ /* legacy {$regex: "...", $options: "..."} */ \ - BS (REGEX) \ - BS (OPTIONS) \ + BS(REGEX) \ + BS(OPTIONS) \ /* modern $regularExpression: {pattern: "...", options: "..."} */ \ - BS (REGULAR_EXPRESSION_PATTERN) \ - BS (REGULAR_EXPRESSION_OPTIONS) \ - BS (CODE) \ - BS (SCOPE) \ - BS (OID) \ - BS (BINARY) \ - BS (TYPE) \ - BS (DATE) \ - BS (TIMESTAMP_T) \ - BS (TIMESTAMP_I) \ - BS (UNDEFINED) \ - BS (MINKEY) \ - BS (MAXKEY) \ - BS (INT32) \ - BS (INT64) \ - BS (DOUBLE) \ - BS (DECIMAL128) \ - BS (DBPOINTER) \ - BS (SYMBOL) \ - BS (UUID) - -typedef enum { FOREACH_BSON_STATE (BSON_STATE_ENUM) } bson_json_read_bson_state_t; - -static const char *bson_state_names[] = {FOREACH_BSON_STATE (GENERATE_STRING)}; + BS(REGULAR_EXPRESSION_PATTERN) \ + BS(REGULAR_EXPRESSION_OPTIONS) \ + BS(CODE) \ + BS(SCOPE) \ + BS(OID) \ + BS(BINARY) \ + BS(TYPE) \ + BS(DATE) \ + BS(TIMESTAMP_T) \ + BS(TIMESTAMP_I) \ + BS(UNDEFINED) \ + BS(MINKEY) \ + BS(MAXKEY) \ + BS(INT32) \ + BS(INT64) \ + BS(DOUBLE) \ + BS(DECIMAL128) \ + BS(DBPOINTER) \ + BS(SYMBOL) \ + BS(UUID) + +typedef enum { FOREACH_BSON_STATE(BSON_STATE_ENUM) } bson_json_read_bson_state_t; + +static const char *bson_state_names[] = {FOREACH_BSON_STATE(GENERATE_STRING)}; typedef struct { uint8_t *buf; @@ -193,10 +200,10 @@ typedef struct { static void -_bson_json_code_cleanup (bson_json_code_t *code_data) +_bson_json_code_cleanup(bson_json_code_t *code_data) { - bson_free (code_data->key_buf.buf); - bson_free (code_data->code_buf.buf); + bson_free(code_data->key_buf.buf); + bson_free(code_data->code_buf.buf); } @@ -249,67 +256,68 @@ typedef struct { /* forward decl */ static void -_bson_json_save_map_key (bson_json_reader_bson_t *bson, const uint8_t *val, size_t len); +_bson_json_save_map_key(bson_json_reader_bson_t *bson, const uint8_t *val, size_t len); static void -_noop (void) +_noop(void) { } #define STACK_ELE(_delta, _name) (bson->stack[(_delta) + bson->n]._name) -#define STACK_BSON(_delta) (((_delta) + bson->n) == 0 ? bson->bson : &STACK_ELE (_delta, bson)) -#define STACK_BSON_PARENT STACK_BSON (-1) -#define STACK_BSON_CHILD STACK_BSON (0) -#define STACK_I STACK_ELE (0, i) -#define STACK_FRAME_TYPE STACK_ELE (0, type) -#define STACK_IS_INITIAL (STACK_FRAME_TYPE == BSON_JSON_FRAME_INITIAL) +#define STACK_BSON(_delta) (((_delta) + bson->n) == 0 ? bson->bson : &STACK_ELE(_delta, bson)) +#define STACK_BSON_PARENT STACK_BSON(-1) +#define STACK_BSON_CHILD STACK_BSON(0) +#define STACK_I STACK_ELE(0, i) +#define STACK_FRAME_TYPE STACK_ELE(0, type) #define STACK_IS_ARRAY (STACK_FRAME_TYPE == BSON_JSON_FRAME_ARRAY) -#define STACK_IS_DOC (STACK_FRAME_TYPE == BSON_JSON_FRAME_DOC) #define STACK_IS_SCOPE (STACK_FRAME_TYPE == BSON_JSON_FRAME_SCOPE) #define STACK_IS_DBPOINTER (STACK_FRAME_TYPE == BSON_JSON_FRAME_DBPOINTER) #define FRAME_TYPE_HAS_BSON(_type) ((_type) == BSON_JSON_FRAME_SCOPE || (_type) == BSON_JSON_FRAME_DBPOINTER) -#define STACK_HAS_BSON FRAME_TYPE_HAS_BSON (STACK_FRAME_TYPE) -#define STACK_PUSH(frame_type) \ - do { \ - if (bson->n >= (STACK_MAX - 1)) { \ - return; \ - } \ - bson->n++; \ - if (STACK_HAS_BSON) { \ - if (FRAME_TYPE_HAS_BSON (frame_type)) { \ - bson_reinit (STACK_BSON_CHILD); \ - } else { \ - bson_destroy (STACK_BSON_CHILD); \ - } \ - } else if (FRAME_TYPE_HAS_BSON (frame_type)) { \ - bson_init (STACK_BSON_CHILD); \ - } \ - STACK_FRAME_TYPE = frame_type; \ +#define STACK_HAS_BSON FRAME_TYPE_HAS_BSON(STACK_FRAME_TYPE) +#define STACK_PUSH(frame_type) \ + do { \ + if (bson->n >= (STACK_MAX - 1)) { \ + return; \ + } \ + bson->n++; \ + mlib_diagnostic_push(); \ + mlib_disable_constant_conditional_expression_warnings(); \ + if (STACK_HAS_BSON) { \ + if (FRAME_TYPE_HAS_BSON(frame_type)) { \ + bson_reinit(STACK_BSON_CHILD); \ + } else { \ + bson_destroy(STACK_BSON_CHILD); \ + } \ + } else if (FRAME_TYPE_HAS_BSON(frame_type)) { \ + bson_init(STACK_BSON_CHILD); \ + } \ + mlib_diagnostic_pop(); \ + STACK_FRAME_TYPE = frame_type; \ } while (0) -#define STACK_PUSH_ARRAY(statement) \ - do { \ - STACK_PUSH (BSON_JSON_FRAME_ARRAY); \ - STACK_I = 0; \ - if (bson->n != 0) { \ - statement; \ - } \ +#define STACK_PUSH_ARRAY(statement) \ + do { \ + STACK_PUSH(BSON_JSON_FRAME_ARRAY); \ + STACK_I = 0; \ + if (bson->n != 0) { \ + statement; \ + } \ } while (0) -#define STACK_PUSH_DOC(statement) \ - do { \ - STACK_PUSH (BSON_JSON_FRAME_DOC); \ - if (bson->n != 0) { \ - statement; \ - } \ +#define STACK_PUSH_DOC(statement) \ + do { \ + STACK_PUSH(BSON_JSON_FRAME_DOC); \ + if (bson->n != 0) { \ + statement; \ + } \ } while (0) -#define STACK_PUSH_SCOPE \ - do { \ - STACK_PUSH (BSON_JSON_FRAME_SCOPE); \ - bson->code_data.in_scope = true; \ +#define STACK_PUSH_SCOPE \ + do { \ + STACK_PUSH(BSON_JSON_FRAME_SCOPE); \ + bson->code_data.in_scope = true; \ } while (0) -#define STACK_PUSH_DBPOINTER \ - do { \ - STACK_PUSH (BSON_JSON_FRAME_DBPOINTER); \ +#define STACK_PUSH_DBPOINTER \ + do { \ + STACK_PUSH(BSON_JSON_FRAME_DBPOINTER); \ } while (0) #define STACK_POP_ARRAY(statement) \ do { \ @@ -339,52 +347,63 @@ _noop (void) } while (0) #define STACK_POP_SCOPE \ do { \ - STACK_POP_DOC (_noop ()); \ + STACK_POP_DOC(_noop()); \ bson->code_data.in_scope = false; \ } while (0) -#define STACK_POP_DBPOINTER STACK_POP_DOC (_noop ()) -#define BASIC_CB_PREAMBLE \ - const char *key; \ - size_t len; \ - bson_json_reader_bson_t *bson = &reader->bson; \ - _bson_json_read_fixup_key (bson); \ - key = bson->key; \ - len = bson->key_buf.len; \ - (void) 0 +#define STACK_POP_DBPOINTER STACK_POP_DOC(_noop()) +#define BASIC_CB_PREAMBLE \ + const char *key; \ + size_t len; \ + bson_json_reader_bson_t *bson = &reader->bson; \ + _bson_json_read_fixup_key(bson); \ + key = bson->key; \ + len = bson->key_buf.len; \ + if (len > INT_MAX) { \ + _bson_json_read_set_error(reader, "Failed to read JSON. key size %zu is too large. Max is %d", len, INT_MAX); \ + return; \ + } \ + (void)0 #define BASIC_CB_BAIL_IF_NOT_NORMAL(_type) \ if (bson->read_state != BSON_JSON_REGULAR) { \ - _bson_json_read_set_error ( \ + _bson_json_read_set_error( \ reader, "Invalid read of %s in state %s", (_type), read_state_names[bson->read_state]); \ return; \ } else if (!key) { \ - _bson_json_read_set_error ( \ + _bson_json_read_set_error( \ reader, "Invalid read of %s without key in state %s", (_type), read_state_names[bson->read_state]); \ return; \ } else \ - (void) 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, \ - "Invalid key \"%s\". Looking for values " \ - "for type \"%s\", got \"%s\"", \ - (_key), \ - _bson_json_type_name (bson->bson_type), \ - _bson_json_type_name (_type)); \ - return; \ - } \ - bson->bson_type = (_type); \ - bson->bson_state = (_state); \ + (void)0 + +#define HANDLE_OPTION_KEY_COMPARE(_key) (len == strlen(_key) && memcmp(key, (_key), len) == 0) + +#define HANDLE_OPTION_TYPE_CHECK(_key, _type) \ + if (bson->bson_type && bson->bson_type != (_type)) { \ + _bson_json_read_set_error(reader, \ + "Invalid key \"%s\". Looking for values " \ + "for type \"%s\", got \"%s\"", \ + (_key), \ + _bson_json_type_name(bson->bson_type), \ + _bson_json_type_name(_type)); \ + return; \ + } \ + ((void)0) + +#define HANDLE_OPTION(_selection_statement, _key, _type, _state) \ + _selection_statement(HANDLE_OPTION_KEY_COMPARE(_key)) \ + { \ + HANDLE_OPTION_TYPE_CHECK(_key, _type); \ + bson->bson_type = (_type); \ + bson->bson_state = (_state); \ } bson_json_opts_t * -bson_json_opts_new (bson_json_mode_t mode, int32_t max_len) +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 = (bson_json_opts_t *)bson_malloc(sizeof *opts); *opts = (bson_json_opts_t){ .mode = mode, .max_len = max_len, @@ -395,104 +414,106 @@ bson_json_opts_new (bson_json_mode_t mode, int32_t max_len) } void -bson_json_opts_destroy (bson_json_opts_t *opts) +bson_json_opts_destroy(bson_json_opts_t *opts) { - bson_free (opts); + bson_free(opts); } static void -_bson_json_read_set_error (bson_json_reader_t *reader, const char *fmt, ...) BSON_GNUC_PRINTF (2, 3); +_bson_json_read_set_error(bson_json_reader_t *reader, const char *fmt, ...) BSON_GNUC_PRINTF(2, 3); static void -_bson_json_read_set_error (bson_json_reader_t *reader, /* IN */ - const char *fmt, /* IN */ - ...) +_bson_json_read_set_error(bson_json_reader_t *reader, /* IN */ + const char *fmt, /* IN */ + ...) { va_list ap; if (reader->error) { reader->error->domain = BSON_ERROR_JSON; reader->error->code = BSON_JSON_ERROR_READ_INVALID_PARAM; - va_start (ap, fmt); - bson_vsnprintf (reader->error->message, sizeof reader->error->message, fmt, ap); - va_end (ap); - reader->error->message[sizeof reader->error->message - 1] = '\0'; + bson_set_error_category(reader->error, BSON_ERROR_CATEGORY); + + va_start(ap, fmt); + bson_vsnprintf(reader->error->message, sizeof reader->error->message, fmt, ap); + va_end(ap); } reader->bson.read_state = BSON_JSON_ERROR; - jsonsl_stop (reader->json); + jsonsl_stop(reader->json); } static void -_bson_json_read_corrupt (bson_json_reader_t *reader, const char *fmt, ...) BSON_GNUC_PRINTF (2, 3); +_bson_json_read_corrupt(bson_json_reader_t *reader, const char *fmt, ...) BSON_GNUC_PRINTF(2, 3); static void -_bson_json_read_corrupt (bson_json_reader_t *reader, /* IN */ - const char *fmt, /* IN */ - ...) +_bson_json_read_corrupt(bson_json_reader_t *reader, /* IN */ + const char *fmt, /* IN */ + ...) { va_list ap; if (reader->error) { reader->error->domain = BSON_ERROR_JSON; reader->error->code = BSON_JSON_ERROR_READ_CORRUPT_JS; - va_start (ap, fmt); - bson_vsnprintf (reader->error->message, sizeof reader->error->message, fmt, ap); - va_end (ap); - reader->error->message[sizeof reader->error->message - 1] = '\0'; + bson_set_error_category(reader->error, BSON_ERROR_CATEGORY); + + va_start(ap, fmt); + bson_vsnprintf(reader->error->message, sizeof reader->error->message, fmt, ap); + va_end(ap); } reader->bson.read_state = BSON_JSON_ERROR; - jsonsl_stop (reader->json); + jsonsl_stop(reader->json); } static void -_bson_json_buf_ensure (bson_json_buf_t *buf, /* IN */ - size_t len) /* IN */ +_bson_json_buf_ensure(bson_json_buf_t *buf, /* IN */ + size_t len) /* IN */ { if (buf->n_bytes < len) { - bson_free (buf->buf); + bson_free(buf->buf); - buf->n_bytes = bson_next_power_of_two (len); - buf->buf = bson_malloc (buf->n_bytes); + buf->n_bytes = bson_next_power_of_two(len); + buf->buf = bson_malloc(buf->n_bytes); } } static void -_bson_json_buf_set (bson_json_buf_t *buf, const void *from, size_t len) +_bson_json_buf_set(bson_json_buf_t *buf, const void *from, size_t len) { - _bson_json_buf_ensure (buf, len + 1); - memcpy (buf->buf, from, len); + _bson_json_buf_ensure(buf, len + 1); + memcpy(buf->buf, from, len); buf->buf[len] = '\0'; buf->len = len; } static void -_bson_json_buf_append (bson_json_buf_t *buf, const void *from, size_t len) +_bson_json_buf_append(bson_json_buf_t *buf, const void *from, size_t len) { size_t len_with_null = len + 1; if (buf->len == 0) { - _bson_json_buf_ensure (buf, len_with_null); + _bson_json_buf_ensure(buf, len_with_null); } else if (buf->n_bytes < buf->len + len_with_null) { - buf->n_bytes = bson_next_power_of_two (buf->len + len_with_null); - buf->buf = bson_realloc (buf->buf, buf->n_bytes); + buf->n_bytes = bson_next_power_of_two(buf->len + len_with_null); + buf->buf = bson_realloc(buf->buf, buf->n_bytes); } - memcpy (buf->buf + buf->len, from, len); + memcpy(buf->buf + buf->len, from, len); buf->len += len; buf->buf[buf->len] = '\0'; } static const char * -_bson_json_type_name (bson_type_t type) +_bson_json_type_name(bson_type_t type) { switch (type) { case BSON_TYPE_EOD: @@ -546,31 +567,31 @@ _bson_json_type_name (bson_type_t type) static void -_bson_json_read_fixup_key (bson_json_reader_bson_t *bson) /* IN */ +_bson_json_read_fixup_key(bson_json_reader_bson_t *bson) /* IN */ { bson_json_read_state_t rs = bson->read_state; if (bson->n >= 0 && STACK_IS_ARRAY && rs == BSON_JSON_REGULAR) { - _bson_json_buf_ensure (&bson->key_buf, 12); - bson->key_buf.len = bson_uint32_to_string (STACK_I, &bson->key, (char *) bson->key_buf.buf, 12); + _bson_json_buf_ensure(&bson->key_buf, 12); + bson->key_buf.len = bson_uint32_to_string(STACK_I, &bson->key, (char *)bson->key_buf.buf, 12); STACK_I++; } } static void -_bson_json_read_null (bson_json_reader_t *reader) +_bson_json_read_null(bson_json_reader_t *reader) { BASIC_CB_PREAMBLE; - BASIC_CB_BAIL_IF_NOT_NORMAL ("null"); + BASIC_CB_BAIL_IF_NOT_NORMAL("null"); - bson_append_null (STACK_BSON_CHILD, key, (int) len); + bson_append_null(STACK_BSON_CHILD, key, (int)len); } static void -_bson_json_read_boolean (bson_json_reader_t *reader, /* IN */ - int val) /* IN */ +_bson_json_read_boolean(bson_json_reader_t *reader, /* IN */ + int val) /* IN */ { BASIC_CB_PREAMBLE; @@ -579,15 +600,15 @@ _bson_json_read_boolean (bson_json_reader_t *reader, /* IN */ return; } - BASIC_CB_BAIL_IF_NOT_NORMAL ("boolean"); + BASIC_CB_BAIL_IF_NOT_NORMAL("boolean"); - bson_append_bool (STACK_BSON_CHILD, key, (int) len, val); + bson_append_bool(STACK_BSON_CHILD, key, (int)len, val); } /* sign is -1 or 1 */ static void -_bson_json_read_integer (bson_json_reader_t *reader, uint64_t val, int64_t sign) +_bson_json_read_integer(bson_json_reader_t *reader, uint64_t val, int64_t sign) { bson_json_read_state_t rs; bson_json_read_bson_state_t bs; @@ -595,11 +616,11 @@ _bson_json_read_integer (bson_json_reader_t *reader, uint64_t val, int64_t sign) BASIC_CB_PREAMBLE; if (sign == 1 && val > INT64_MAX) { - _bson_json_read_set_error (reader, "Number \"%" PRIu64 "\" is out of range", val); + _bson_json_read_set_error(reader, "Number \"%" PRIu64 "\" is out of range", val); return; - } else if (sign == -1 && val > ((uint64_t) INT64_MAX + 1)) { - _bson_json_read_set_error (reader, "Number \"-%" PRIu64 "\" is out of range", val); + } else if (sign == -1 && val > ((uint64_t)INT64_MAX + 1)) { + _bson_json_read_set_error(reader, "Number \"-%" PRIu64 "\" is out of range", val); return; } @@ -608,20 +629,20 @@ _bson_json_read_integer (bson_json_reader_t *reader, uint64_t val, int64_t sign) bs = bson->bson_state; if (rs == BSON_JSON_REGULAR) { - BASIC_CB_BAIL_IF_NOT_NORMAL ("integer"); + BASIC_CB_BAIL_IF_NOT_NORMAL("integer"); - if (val <= INT32_MAX || (sign == -1 && val <= (uint64_t) INT32_MAX + 1)) { - bson_append_int32 (STACK_BSON_CHILD, key, (int) len, (int) (val * sign)); + if (val <= INT32_MAX || (sign == -1 && val <= (uint64_t)INT32_MAX + 1)) { + bson_append_int32(STACK_BSON_CHILD, key, (int)len, (int32_t)((int64_t)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); + 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); + 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); + bson_append_int64(STACK_BSON_CHILD, key, (int)len, (int64_t)val); } } else if (rs == BSON_JSON_IN_BSON_TYPE || rs == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES) { switch (bs) { @@ -631,48 +652,48 @@ _bson_json_read_integer (bson_json_reader_t *reader, uint64_t val, int64_t sign) break; case BSON_JSON_LF_TIMESTAMP_T: if (sign == -1) { - _bson_json_read_set_error (reader, "Invalid timestamp value: \"-%" PRIu64 "\"", val); + _bson_json_read_set_error(reader, "Invalid timestamp value: \"-%" PRIu64 "\"", val); return; } bson->bson_type_data.timestamp.has_t = true; - bson->bson_type_data.timestamp.t = (uint32_t) val; + bson->bson_type_data.timestamp.t = (uint32_t)val; break; case BSON_JSON_LF_TIMESTAMP_I: if (sign == -1) { - _bson_json_read_set_error (reader, "Invalid timestamp value: \"-%" PRIu64 "\"", val); + _bson_json_read_set_error(reader, "Invalid timestamp value: \"-%" PRIu64 "\"", val); return; } bson->bson_type_data.timestamp.has_i = true; - bson->bson_type_data.timestamp.i = (uint32_t) val; + bson->bson_type_data.timestamp.i = (uint32_t)val; break; case BSON_JSON_LF_MINKEY: if (sign == -1) { - _bson_json_read_set_error (reader, "Invalid MinKey value: \"-%" PRIu64 "\"", val); + _bson_json_read_set_error(reader, "Invalid MinKey value: \"-%" PRIu64 "\"", val); return; } else if (val != 1) { - _bson_json_read_set_error (reader, "Invalid MinKey value: \"%" PRIu64 "\"", val); + _bson_json_read_set_error(reader, "Invalid MinKey value: \"%" PRIu64 "\"", val); } bson->bson_type_data.minkey.has_minkey = true; break; case BSON_JSON_LF_MAXKEY: if (sign == -1) { - _bson_json_read_set_error (reader, "Invalid MinKey value: \"-%" PRIu64 "\"", val); + _bson_json_read_set_error(reader, "Invalid MinKey value: \"-%" PRIu64 "\"", val); return; } else if (val != 1) { - _bson_json_read_set_error (reader, "Invalid MinKey value: \"%" PRIu64 "\"", val); + _bson_json_read_set_error(reader, "Invalid MinKey value: \"%" PRIu64 "\"", val); } bson->bson_type_data.maxkey.has_maxkey = true; break; case BSON_JSON_LF_INT32: case BSON_JSON_LF_INT64: - _bson_json_read_set_error (reader, - "Invalid state for integer read: %s, " - "expected number as quoted string like \"123\"", - bson_state_names[bs]); + _bson_json_read_set_error(reader, + "Invalid state for integer read: %s, " + "expected number as quoted string like \"123\"", + bson_state_names[bs]); break; case BSON_JSON_LF_REGEX: case BSON_JSON_LF_OPTIONS: @@ -690,24 +711,24 @@ _bson_json_read_integer (bson_json_reader_t *reader, uint64_t val, int64_t sign) case BSON_JSON_LF_DBPOINTER: case BSON_JSON_LF_SYMBOL: default: - _bson_json_read_set_error (reader, - "Unexpected integer %s%" PRIu64 " in type \"%s\"", - sign == -1 ? "-" : "", - val, - _bson_json_type_name (bson->bson_type)); + _bson_json_read_set_error(reader, + "Unexpected integer %s%" PRIu64 " in type \"%s\"", + sign == -1 ? "-" : "", + val, + _bson_json_type_name(bson->bson_type)); } } else { - _bson_json_read_set_error ( + _bson_json_read_set_error( reader, "Unexpected integer %s%" PRIu64 " in state \"%s\"", sign == -1 ? "-" : "", val, read_state_names[rs]); } } static bool -_bson_json_parse_double (bson_json_reader_t *reader, const char *val, size_t vlen, double *d) +_bson_json_parse_double(bson_json_reader_t *reader, const char *val, size_t vlen, double *d) { errno = 0; - *d = strtod (val, NULL); + *d = strtod(val, NULL); #ifdef _MSC_VER const double pos_inf = INFINITY; @@ -715,28 +736,28 @@ _bson_json_parse_double (bson_json_reader_t *reader, const char *val, size_t vle /* Microsoft's strtod parses "NaN", "Infinity", "-Infinity" as 0 */ if (*d == 0.0) { - if (!_strnicmp (val, "nan", vlen)) { + if (!_strnicmp(val, "nan", vlen)) { *d = NAN; return true; - } else if (!_strnicmp (val, "infinity", vlen)) { + } else if (!_strnicmp(val, "infinity", vlen)) { *d = pos_inf; return true; - } else if (!_strnicmp (val, "-infinity", vlen)) { + } else if (!_strnicmp(val, "-infinity", vlen)) { *d = neg_inf; return true; } } if ((*d == HUGE_VAL || *d == -HUGE_VAL) && errno == ERANGE) { - _bson_json_read_set_error (reader, "Number \"%.*s\" is out of range", (int) vlen, val); + _bson_json_read_set_error(reader, "Number \"%.*s\" is out of range", (int)vlen, val); return false; } #else /* not MSVC - set err on overflow, but avoid err for infinity */ - if ((*d == HUGE_VAL || *d == -HUGE_VAL) && errno == ERANGE && strncasecmp (val, "infinity", vlen) && - strncasecmp (val, "-infinity", vlen)) { - _bson_json_read_set_error (reader, "Number \"%.*s\" is out of range", (int) vlen, val); + if ((*d == HUGE_VAL || *d == -HUGE_VAL) && errno == ERANGE && strncasecmp(val, "infinity", vlen) && + strncasecmp(val, "-infinity", vlen)) { + _bson_json_read_set_error(reader, "Number \"%.*s\" is out of range", (int)vlen, val); return false; } @@ -747,38 +768,38 @@ _bson_json_parse_double (bson_json_reader_t *reader, const char *val, size_t vle static void -_bson_json_read_double (bson_json_reader_t *reader, /* IN */ - double val) /* IN */ +_bson_json_read_double(bson_json_reader_t *reader, /* IN */ + double val) /* IN */ { BASIC_CB_PREAMBLE; - BASIC_CB_BAIL_IF_NOT_NORMAL ("double"); + BASIC_CB_BAIL_IF_NOT_NORMAL("double"); - if (!bson_append_double (STACK_BSON_CHILD, key, (int) len, val)) { - _bson_json_read_set_error (reader, "Cannot append double value %g", val); + if (!bson_append_double(STACK_BSON_CHILD, key, (int)len, val)) { + _bson_json_read_set_error(reader, "Cannot append double value %g", val); } } static bool -_bson_json_read_int64_or_set_error (bson_json_reader_t *reader, /* IN */ - const unsigned char *val, /* IN */ - size_t vlen, /* IN */ - int64_t *v64) /* OUT */ +_bson_json_read_int64_or_set_error(bson_json_reader_t *reader, /* IN */ + const unsigned char *val, /* IN */ + size_t vlen, /* IN */ + int64_t *v64) /* OUT */ { bson_json_reader_bson_t *bson = &reader->bson; char *endptr = NULL; - _bson_json_read_fixup_key (bson); + _bson_json_read_fixup_key(bson); errno = 0; - *v64 = bson_ascii_strtoll ((const char *) val, &endptr, 10); + *v64 = bson_ascii_strtoll((const char *)val, &endptr, 10); if (((*v64 == INT64_MIN) || (*v64 == INT64_MAX)) && (errno == ERANGE)) { - _bson_json_read_set_error (reader, "Number \"%s\" is out of range", val); + _bson_json_read_set_error(reader, "Number \"%s\" is out of range", val); return false; } - if (endptr != ((const char *) val + vlen)) { - _bson_json_read_set_error (reader, "Number \"%s\" is invalid", val); + if (endptr != ((const char *)val + vlen)) { + _bson_json_read_set_error(reader, "Number \"%s\" is invalid", val); return false; } @@ -786,20 +807,20 @@ _bson_json_read_int64_or_set_error (bson_json_reader_t *reader, /* IN */ } static bool -_unhexlify_uuid (const char *uuid, uint8_t *out, size_t max) +_unhexlify_uuid(const char *uuid, uint8_t *out, size_t max) { unsigned int byte; size_t x = 0; int i = 0; - BSON_ASSERT (strlen (uuid) == 32); + BSON_ASSERT(strlen(uuid) == 32); - while (SSCANF (&uuid[i], "%2x", &byte) == 1) { + while (SSCANF(&uuid[i], "%2x", &byte) == 1) { if (x >= max) { return false; } - out[x++] = (uint8_t) byte; + out[x++] = (uint8_t)byte; i += 2; } @@ -809,7 +830,7 @@ _unhexlify_uuid (const char *uuid, uint8_t *out, size_t max) /* parse a value for "base64", "subType", legacy "$binary" or "$type", or * "$uuid" */ static void -_bson_json_parse_binary_elem (bson_json_reader_t *reader, const char *val_w_null, size_t vlen) +_bson_json_parse_binary_elem(bson_json_reader_t *reader, const char *val_w_null, size_t vlen) { bson_json_read_bson_state_t bs; bson_json_bson_data_t *data; @@ -822,33 +843,33 @@ _bson_json_parse_binary_elem (bson_json_reader_t *reader, const char *val_w_null if (bs == BSON_JSON_LF_BINARY) { data->binary.has_binary = true; - binary_len = mcommon_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 ( + _bson_json_read_set_error( reader, "Invalid input string \"%s\", looking for base64-encoded binary", val_w_null); } - _bson_json_buf_ensure (&bson->bson_type_buf[0], (size_t) binary_len + 1); - if (mcommon_b64_pton (val_w_null, bson->bson_type_buf[0].buf, (size_t) binary_len + 1) < 0) { - _bson_json_read_set_error ( + _bson_json_buf_ensure(&bson->bson_type_buf[0], (size_t)binary_len + 1); + 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", val_w_null); } - bson->bson_type_buf[0].len = (size_t) binary_len; + bson->bson_type_buf[0].len = (size_t)binary_len; } else if (bs == BSON_JSON_LF_TYPE) { data->binary.has_subtype = true; - if (SSCANF (val_w_null, "%02x", &data->binary.type) != 1) { + if (SSCANF(val_w_null, "%02x", &data->binary.type) != 1) { if (!data->binary.is_legacy || data->binary.has_binary) { /* misformatted subtype, like {$binary: {base64: "", subType: "x"}}, * or legacy {$binary: "", $type: "x"} */ - _bson_json_read_set_error (reader, "Invalid input string \"%s\", looking for binary subtype", val_w_null); + _bson_json_read_set_error(reader, "Invalid input string \"%s\", looking for binary subtype", val_w_null); } else { /* actually a query operator: {x: {$type: "array"}}*/ bson->read_state = BSON_JSON_REGULAR; - STACK_PUSH_DOC (bson_append_document_begin (STACK_BSON_PARENT, key, (int) len, STACK_BSON_CHILD)); + STACK_PUSH_DOC(bson_append_document_begin(STACK_BSON_PARENT, key, (int)len, STACK_BSON_CHILD)); - bson_append_utf8 (STACK_BSON_CHILD, "$type", 5, (const char *) val_w_null, (int) vlen); + bson_append_utf8(STACK_BSON_CHILD, "$type", 5, (const char *)val_w_null, (int)vlen); } } } else if (bs == BSON_JSON_LF_UUID) { @@ -862,50 +883,50 @@ _bson_json_parse_binary_elem (bson_json_reader_t *reader, const char *val_w_null /* Validate the UUID and extract relevant portions */ /* We can't use %x here as it allows +, -, and 0x prefixes */ #ifdef _MSC_VER - SSCANF (val_w_null, - "%8c-%4c-%4c-%4c-%12c%n", - &uuid[0], - 8, - &uuid[8], - 4, - &uuid[12], - 4, - &uuid[16], - 4, - &uuid[20], - 12, - &nread); + SSCANF(val_w_null, + "%8c-%4c-%4c-%4c-%12c%n", + &uuid[0], + 8, + &uuid[8], + 4, + &uuid[12], + 4, + &uuid[16], + 4, + &uuid[20], + 12, + &nread); #else - SSCANF (val_w_null, "%8c-%4c-%4c-%4c-%12c%n", &uuid[0], &uuid[8], &uuid[12], &uuid[16], &uuid[20], &nread); + SSCANF(val_w_null, "%8c-%4c-%4c-%4c-%12c%n", &uuid[0], &uuid[8], &uuid[12], &uuid[16], &uuid[20], &nread); #endif uuid[32] = '\0'; if (nread != 36 || val_w_null[nread] != '\0') { - _bson_json_read_set_error (reader, - "Invalid input string \"%s\", looking for " - "a dash-separated UUID string", - val_w_null); + _bson_json_read_set_error(reader, + "Invalid input string \"%s\", looking for " + "a dash-separated UUID string", + val_w_null); return; } binary_len = 16; - _bson_json_buf_ensure (&bson->bson_type_buf[0], (size_t) binary_len + 1); + _bson_json_buf_ensure(&bson->bson_type_buf[0], (size_t)binary_len + 1); - if (!_unhexlify_uuid (&uuid[0], bson->bson_type_buf[0].buf, (size_t) binary_len)) { - _bson_json_read_set_error (reader, - "Invalid input string \"%s\", looking for " - "a dash-separated UUID string", - val_w_null); + if (!_unhexlify_uuid(&uuid[0], bson->bson_type_buf[0].buf, (size_t)binary_len)) { + _bson_json_read_set_error(reader, + "Invalid input string \"%s\", looking for " + "a dash-separated UUID string", + val_w_null); } - bson->bson_type_buf[0].len = (size_t) binary_len; + bson->bson_type_buf[0].len = (size_t)binary_len; } } static bool -_bson_json_allow_embedded_nulls (bson_json_reader_t const *reader) +_bson_json_allow_embedded_nulls(bson_json_reader_t const *reader) { const bson_json_read_state_t read_state = reader->bson.read_state; const bson_json_read_bson_state_t bson_state = reader->bson.bson_state; @@ -933,40 +954,40 @@ _bson_json_allow_embedded_nulls (bson_json_reader_t const *reader) } static void -_bson_json_read_string (bson_json_reader_t *reader, /* IN */ - const unsigned char *val, /* IN */ - size_t vlen) /* IN */ +_bson_json_read_string(bson_json_reader_t *reader, /* IN */ + const unsigned char *val, /* IN */ + size_t vlen) /* IN */ { bson_json_read_state_t rs; bson_json_read_bson_state_t bs; - const bool allow_null = _bson_json_allow_embedded_nulls (reader); + const bool allow_null = _bson_json_allow_embedded_nulls(reader); BASIC_CB_PREAMBLE; rs = bson->read_state; bs = bson->bson_state; - if (!bson_utf8_validate ((const char *) val, vlen, allow_null)) { - _bson_json_read_corrupt (reader, "invalid bytes in UTF8 string"); + if (!bson_utf8_validate((const char *)val, vlen, allow_null)) { + _bson_json_read_corrupt(reader, "invalid bytes in UTF8 string"); return; } if (rs == BSON_JSON_REGULAR) { - BASIC_CB_BAIL_IF_NOT_NORMAL ("string"); - bson_append_utf8 (STACK_BSON_CHILD, key, (int) len, (const char *) val, (int) vlen); + BASIC_CB_BAIL_IF_NOT_NORMAL("string"); + bson_append_utf8(STACK_BSON_CHILD, key, (int)len, (const char *)val, (int)vlen); } else if (rs == BSON_JSON_IN_BSON_TYPE_SCOPE_STARTMAP || rs == BSON_JSON_IN_BSON_TYPE_DBPOINTER_STARTMAP) { - _bson_json_read_set_error (reader, "Invalid read of \"%s\" in state \"%s\"", val, read_state_names[rs]); + _bson_json_read_set_error(reader, "Invalid read of \"%s\" in state \"%s\"", val, read_state_names[rs]); } else if (rs == BSON_JSON_IN_BSON_TYPE_BINARY_VALUES) { const char *val_w_null; - _bson_json_buf_set (&bson->bson_type_buf[2], val, vlen); - val_w_null = (const char *) bson->bson_type_buf[2].buf; + _bson_json_buf_set(&bson->bson_type_buf[2], val, vlen); + val_w_null = (const char *)bson->bson_type_buf[2].buf; - _bson_json_parse_binary_elem (reader, val_w_null, vlen); + _bson_json_parse_binary_elem(reader, val_w_null, vlen); } else if (rs == BSON_JSON_IN_BSON_TYPE || rs == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES || rs == BSON_JSON_IN_BSON_TYPE_REGEX_VALUES || rs == BSON_JSON_IN_BSON_TYPE_DATE_NUMBERLONG) { const char *val_w_null; - _bson_json_buf_set (&bson->bson_type_buf[2], val, vlen); - val_w_null = (const char *) bson->bson_type_buf[2].buf; + _bson_json_buf_set(&bson->bson_type_buf[2], val, vlen); + val_w_null = (const char *)bson->bson_type_buf[2].buf; switch (bs) { case BSON_JSON_LF_REGEX: @@ -974,14 +995,14 @@ _bson_json_read_string (bson_json_reader_t *reader, /* IN */ /* FALL THROUGH */ case BSON_JSON_LF_REGULAR_EXPRESSION_PATTERN: bson->bson_type_data.regex.has_pattern = true; - _bson_json_buf_set (&bson->bson_type_buf[0], val, vlen); + _bson_json_buf_set(&bson->bson_type_buf[0], val, vlen); break; case BSON_JSON_LF_OPTIONS: bson->bson_type_data.regex.is_legacy = true; /* FALL THROUGH */ case BSON_JSON_LF_REGULAR_EXPRESSION_OPTIONS: bson->bson_type_data.regex.has_options = true; - _bson_json_buf_set (&bson->bson_type_buf[1], val, vlen); + _bson_json_buf_set(&bson->bson_type_buf[1], val, vlen); break; case BSON_JSON_LF_OID: @@ -990,18 +1011,18 @@ _bson_json_read_string (bson_json_reader_t *reader, /* IN */ } bson->bson_type_data.oid.has_oid = true; - bson_oid_init_from_string (&bson->bson_type_data.oid.oid, val_w_null); + bson_oid_init_from_string(&bson->bson_type_data.oid.oid, val_w_null); break; case BSON_JSON_LF_BINARY: case BSON_JSON_LF_TYPE: bson->bson_type_data.binary.is_legacy = true; /* FALL THROUGH */ case BSON_JSON_LF_UUID: - _bson_json_parse_binary_elem (reader, val_w_null, vlen); + _bson_json_parse_binary_elem(reader, val_w_null, vlen); break; case BSON_JSON_LF_INT32: { int64_t v64; - if (!_bson_json_read_int64_or_set_error (reader, val, vlen, &v64)) { + if (!_bson_json_read_int64_or_set_error(reader, val, vlen, &v64)) { /* the error is set, return and let the reader exit */ return; } @@ -1011,14 +1032,14 @@ _bson_json_read_string (bson_json_reader_t *reader, /* IN */ } if (bson->read_state == BSON_JSON_IN_BSON_TYPE) { - bson->bson_type_data.v_int32.value = (int32_t) v64; + bson->bson_type_data.v_int32.value = (int32_t)v64; } else { goto BAD_PARSE; } } break; case BSON_JSON_LF_INT64: { int64_t v64; - if (!_bson_json_read_int64_or_set_error (reader, val, vlen, &v64)) { + if (!_bson_json_read_int64_or_set_error(reader, val, vlen, &v64)) { /* the error is set, return and let the reader exit */ return; } @@ -1033,7 +1054,7 @@ _bson_json_read_string (bson_json_reader_t *reader, /* IN */ } } break; case BSON_JSON_LF_DOUBLE: { - if (!_bson_json_parse_double (reader, (const char *) val, vlen, &bson->bson_type_data.v_double.value)) { + if (!_bson_json_parse_double(reader, (const char *)val, vlen, &bson->bson_type_data.v_double.value)) { /* the error is set, return and let the reader exit */ return; } @@ -1041,8 +1062,8 @@ _bson_json_read_string (bson_json_reader_t *reader, /* IN */ case BSON_JSON_LF_DATE: { int64_t v64; - if (!_bson_iso8601_date_parse ((char *) val, (int) vlen, &v64, reader->error)) { - jsonsl_stop (reader->json); + if (!_bson_iso8601_date_parse((char *)val, (int)vlen, &v64, reader->error)) { + jsonsl_stop(reader->json); } else { bson->bson_type_data.date.has_date = true; bson->bson_type_data.date.date = v64; @@ -1051,17 +1072,17 @@ _bson_json_read_string (bson_json_reader_t *reader, /* IN */ case BSON_JSON_LF_DECIMAL128: { bson_decimal128_t decimal128; - if (bson_decimal128_from_string (val_w_null, &decimal128) && bson->read_state == BSON_JSON_IN_BSON_TYPE) { + if (bson_decimal128_from_string(val_w_null, &decimal128) && bson->read_state == BSON_JSON_IN_BSON_TYPE) { bson->bson_type_data.v_decimal128.value = decimal128; } else { goto BAD_PARSE; } } break; case BSON_JSON_LF_CODE: - _bson_json_buf_set (&bson->code_data.code_buf, val, vlen); + _bson_json_buf_set(&bson->code_data.code_buf, val, vlen); break; case BSON_JSON_LF_SYMBOL: - bson_append_symbol (STACK_BSON_CHILD, key, (int) len, (const char *) val, (int) vlen); + bson_append_symbol(STACK_BSON_CHILD, key, (int)len, (const char *)val, (int)vlen); break; case BSON_JSON_LF_SCOPE: case BSON_JSON_LF_TIMESTAMP_T: @@ -1076,16 +1097,16 @@ _bson_json_read_string (bson_json_reader_t *reader, /* IN */ return; BAD_PARSE: - _bson_json_read_set_error ( + _bson_json_read_set_error( reader, "Invalid input string \"%s\", looking for %s", val_w_null, bson_state_names[bs]); } else { - _bson_json_read_set_error (reader, "Invalid state to look for string: %s", read_state_names[rs]); + _bson_json_read_set_error(reader, "Invalid state to look for string: %s", read_state_names[rs]); } } static void -_bson_json_read_start_map (bson_json_reader_t *reader) /* IN */ +_bson_json_read_start_map(bson_json_reader_t *reader) /* IN */ { BASIC_CB_PREAMBLE; @@ -1102,9 +1123,9 @@ _bson_json_read_start_map (bson_json_reader_t *reader) /* IN */ * 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, (int) len, STACK_BSON_CHILD)); - _bson_json_save_map_key (bson, (const uint8_t *) "$type", 5); + BSON_ASSERT(mlib_in_range(int, len)); + STACK_PUSH_DOC(bson_append_document_begin(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: @@ -1130,8 +1151,7 @@ _bson_json_read_start_map (bson_json_reader_t *reader) /* IN */ 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); + _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: @@ -1139,7 +1159,7 @@ _bson_json_read_start_map (bson_json_reader_t *reader) /* IN */ 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"); + 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; @@ -1154,40 +1174,57 @@ _bson_json_read_start_map (bson_json_reader_t *reader) /* IN */ } /* silence some warnings */ - (void) len; - (void) key; + (void)len; + (void)key; } -static bool -_is_known_key (const char *key, size_t len) -{ - bool ret; +#define BSON_PRIVATE_SPECIAL_KEYS_XMACRO(X) \ + X(binary) \ + X(code) \ + X(date) \ + X(dbPointer) \ + X(maxKey) \ + X(minKey) \ + X(numberDecimal) \ + X(numberDouble) \ + X(numberInt) \ + X(numberLong) \ + X(oid) \ + X(options) \ + X(regex) \ + X(regularExpression) \ + X(scope) \ + X(symbol) \ + X(timestamp) \ + X(type) \ + X(undefined) \ + X(uuid) -#define IS_KEY(k) (len == strlen (k) && (0 == memcmp (k, key, len))) - - ret = (IS_KEY ("$regularExpression") || IS_KEY ("$regex") || IS_KEY ("$options") || IS_KEY ("$code") || - IS_KEY ("$scope") || IS_KEY ("$oid") || IS_KEY ("$binary") || IS_KEY ("$type") || IS_KEY ("$date") || - IS_KEY ("$undefined") || IS_KEY ("$maxKey") || IS_KEY ("$minKey") || IS_KEY ("$timestamp") || - IS_KEY ("$numberInt") || IS_KEY ("$numberLong") || IS_KEY ("$numberDouble") || IS_KEY ("$numberDecimal") || - IS_KEY ("$numberInt") || IS_KEY ("$numberLong") || IS_KEY ("$numberDouble") || IS_KEY ("$numberDecimal") || - IS_KEY ("$dbPointer") || IS_KEY ("$symbol") || IS_KEY ("$uuid")); +static bool +_is_known_key(const char *key, size_t len) +{ +#define IS_KEY(k) \ + if (len == strlen("$" #k) && (0 == memcmp("$" #k, key, len))) { \ + return true; \ + } + BSON_PRIVATE_SPECIAL_KEYS_XMACRO(IS_KEY) #undef IS_KEY - return ret; + return false; } static void -_bson_json_save_map_key (bson_json_reader_bson_t *bson, const uint8_t *val, size_t len) +_bson_json_save_map_key(bson_json_reader_bson_t *bson, const uint8_t *val, size_t len) { - _bson_json_buf_set (&bson->key_buf, val, len); - bson->key = (const char *) bson->key_buf.buf; + _bson_json_buf_set(&bson->key_buf, val, len); + bson->key = (const char *)bson->key_buf.buf; } static void -_bson_json_read_code_or_scope_key (bson_json_reader_bson_t *bson, bool is_scope, const uint8_t *val, size_t len) +_bson_json_read_code_or_scope_key(bson_json_reader_bson_t *bson, bool is_scope, const uint8_t *val, size_t len) { bson_json_code_t *code = &bson->code_data; @@ -1195,14 +1232,14 @@ _bson_json_read_code_or_scope_key (bson_json_reader_bson_t *bson, bool is_scope, /* we're reading something weirdly nested, e.g. we just read "$code" in * "$scope: {x: {$code: {}}}". just create the subdoc within the scope. */ bson->read_state = BSON_JSON_REGULAR; - STACK_PUSH_DOC ( - bson_append_document_begin (STACK_BSON_PARENT, bson->key, (int) bson->key_buf.len, STACK_BSON_CHILD)); - _bson_json_save_map_key (bson, val, len); + STACK_PUSH_DOC( + bson_append_document_begin(STACK_BSON_PARENT, bson->key, (int)bson->key_buf.len, STACK_BSON_CHILD)); + _bson_json_save_map_key(bson, val, len); } else { if (!bson->code_data.key_buf.len) { /* save the key, e.g. {"key": {"$code": "return x", "$scope":{"x":1}}}, * in case it is overwritten while parsing scope sub-object */ - _bson_json_buf_set (&bson->code_data.key_buf, bson->key, bson->key_buf.len); + _bson_json_buf_set(&bson->code_data.key_buf, bson->key, bson->key_buf.len); } if (is_scope) { @@ -1220,196 +1257,209 @@ _bson_json_read_code_or_scope_key (bson_json_reader_bson_t *bson, bool is_scope, static void -_bson_json_bad_key_in_type (bson_json_reader_t *reader, /* IN */ - const uint8_t *val) /* IN */ +_bson_json_bad_key_in_type(bson_json_reader_t *reader, /* IN */ + const uint8_t *val) /* IN */ { bson_json_reader_bson_t *bson = &reader->bson; - _bson_json_read_set_error ( - reader, "Invalid key \"%s\". Looking for values for type \"%s\"", val, _bson_json_type_name (bson->bson_type)); + _bson_json_read_set_error( + reader, "Invalid key \"%s\". Looking for values for type \"%s\"", val, _bson_json_type_name(bson->bson_type)); } static void -_bson_json_read_map_key (bson_json_reader_t *reader, /* IN */ - const uint8_t *val, /* IN */ - size_t len) /* IN */ +_bson_json_read_map_key(bson_json_reader_t *reader, /* IN */ + const uint8_t *val, /* IN */ + size_t len) /* IN */ { bson_json_reader_bson_t *bson = &reader->bson; - if (!bson_utf8_validate ((const char *) val, len, false /* allow null */)) { - _bson_json_read_corrupt (reader, "invalid bytes in UTF8 string"); + if (!bson_utf8_validate((const char *)val, len, false /* allow null */)) { + _bson_json_read_corrupt(reader, "invalid bytes in UTF8 string"); return; } + const char *const key = (const char *)val; + if (bson->read_state == BSON_JSON_IN_START_MAP) { - if (len > 0 && val[0] == '$' && _is_known_key ((const char *) val, len) && - bson->n >= 0 /* key is in subdocument */) { + if (len > 0 && key[0] == '$' && _is_known_key(key, len) && bson->n >= 0 /* key is in subdocument */) { bson->read_state = BSON_JSON_IN_BSON_TYPE; - bson->bson_type = (bson_type_t) 0; - memset (&bson->bson_type_data, 0, sizeof bson->bson_type_data); + bson->bson_type = (bson_type_t)0; + memset(&bson->bson_type_data, 0, sizeof bson->bson_type_data); } else { bson->read_state = BSON_JSON_REGULAR; - STACK_PUSH_DOC ( - bson_append_document_begin (STACK_BSON_PARENT, bson->key, (int) bson->key_buf.len, STACK_BSON_CHILD)); + STACK_PUSH_DOC( + bson_append_document_begin(STACK_BSON_PARENT, bson->key, (int)bson->key_buf.len, STACK_BSON_CHILD)); } } else if (bson->read_state == BSON_JSON_IN_SCOPE) { /* we've read "key" in {$code: "", $scope: {key: ""}}*/ bson->read_state = BSON_JSON_REGULAR; STACK_PUSH_SCOPE; - _bson_json_save_map_key (bson, val, len); + _bson_json_save_map_key(bson, val, len); } else if (bson->read_state == BSON_JSON_IN_DBPOINTER) { /* we've read "$ref" or "$id" in {$dbPointer: {$ref: ..., $id: ...}} */ bson->read_state = BSON_JSON_REGULAR; STACK_PUSH_DBPOINTER; - _bson_json_save_map_key (bson, val, len); + _bson_json_save_map_key(bson, val, len); } if (bson->read_state == BSON_JSON_IN_BSON_TYPE) { - 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)) + 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 (HANDLE_OPTION_KEY_COMPARE("$timestamp")) { + HANDLE_OPTION_TYPE_CHECK("$timestamp", BSON_TYPE_TIMESTAMP); 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 (HANDLE_OPTION_KEY_COMPARE("$regularExpression")) { + HANDLE_OPTION_TYPE_CHECK("$regularExpression", BSON_TYPE_REGEX); 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 (HANDLE_OPTION_KEY_COMPARE("$dbPointer")) { + HANDLE_OPTION_TYPE_CHECK("$dbPointer", BSON_TYPE_DBPOINTER); + /* start parsing "key": {"$dbPointer": {...}}, save "key" for later */ - _bson_json_buf_set (&bson->dbpointer_key, bson->key, bson->key_buf.len); + _bson_json_buf_set(&bson->dbpointer_key, bson->key, 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 (HANDLE_OPTION_KEY_COMPARE("$code")) { - _bson_json_read_code_or_scope_key (bson, false /* is_scope */, val, len); + // "$code" may come after "$scope". + if (bson->bson_type != BSON_TYPE_CODEWSCOPE) { + HANDLE_OPTION_TYPE_CHECK("$code", BSON_TYPE_CODE); + } + _bson_json_read_code_or_scope_key(bson, false /* is_scope */, val, len); } - else if (!strcmp ("$scope", (const char *) val)) + else if (HANDLE_OPTION_KEY_COMPARE("$scope")) { - _bson_json_read_code_or_scope_key (bson, true /* is_scope */, val, len); + // "$scope" may come after "$code". + if (bson->bson_type != BSON_TYPE_CODE) { + HANDLE_OPTION_TYPE_CHECK("$scope", BSON_TYPE_CODEWSCOPE); + } + _bson_json_read_code_or_scope_key(bson, true /* is_scope */, val, len); } else { - _bson_json_bad_key_in_type (reader, val); + _bson_json_bad_key_in_type(reader, val); } } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_DATE_NUMBERLONG) { - HANDLE_OPTION (if, "$numberLong", BSON_TYPE_DATE_TIME, BSON_JSON_LF_INT64) + HANDLE_OPTION(if, "$numberLong", BSON_TYPE_DATE_TIME, BSON_JSON_LF_INT64) else { - _bson_json_bad_key_in_type (reader, val); + _bson_json_bad_key_in_type(reader, val); } } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES) { - 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) + 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); + _bson_json_bad_key_in_type(reader, val); } } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_REGEX_VALUES) { - 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) + 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); + _bson_json_bad_key_in_type(reader, val); } } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_BINARY_VALUES) { - HANDLE_OPTION (if, "base64", BSON_TYPE_BINARY, BSON_JSON_LF_BINARY) - HANDLE_OPTION (else if, "subType", BSON_TYPE_BINARY, BSON_JSON_LF_TYPE) + 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); + _bson_json_bad_key_in_type(reader, val); } } else { - _bson_json_save_map_key (bson, val, len); + _bson_json_save_map_key(bson, val, len); } } static void -_bson_json_read_append_binary (bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ +_bson_json_read_append_binary(bson_json_reader_t *reader, /* IN */ + bson_json_reader_bson_t *bson) /* IN */ { bson_json_bson_data_t *data = &bson->bson_type_data; if (data->binary.is_legacy) { if (!data->binary.has_binary) { - _bson_json_read_set_error (reader, "Missing \"$binary\" after \"$type\" reading type \"binary\""); + _bson_json_read_set_error(reader, "Missing \"$binary\" after \"$type\" reading type \"binary\""); return; } else if (!data->binary.has_subtype) { - _bson_json_read_set_error (reader, "Missing \"$type\" after \"$binary\" reading type \"binary\""); + _bson_json_read_set_error(reader, "Missing \"$type\" after \"$binary\" reading type \"binary\""); return; } } else { if (!data->binary.has_binary) { - _bson_json_read_set_error (reader, "Missing \"base64\" after \"subType\" reading type \"binary\""); + _bson_json_read_set_error(reader, "Missing \"base64\" after \"subType\" reading type \"binary\""); return; } else if (!data->binary.has_subtype) { - _bson_json_read_set_error (reader, "Missing \"subType\" after \"base64\" reading type \"binary\""); + _bson_json_read_set_error(reader, "Missing \"subType\" after \"base64\" reading type \"binary\""); return; } } - if (!bson_append_binary (STACK_BSON_CHILD, - bson->key, - (int) bson->key_buf.len, - data->binary.type, - bson->bson_type_buf[0].buf, - (uint32_t) bson->bson_type_buf[0].len)) { - _bson_json_read_set_error (reader, "Error storing binary data"); + if (!bson_append_binary(STACK_BSON_CHILD, + bson->key, + (int)bson->key_buf.len, + data->binary.type, + bson->bson_type_buf[0].buf, + (uint32_t)bson->bson_type_buf[0].len)) { + _bson_json_read_set_error(reader, "Error storing binary data"); } } static void -_bson_json_read_append_regex (bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ +_bson_json_read_append_regex(bson_json_reader_t *reader, /* IN */ + bson_json_reader_bson_t *bson) /* IN */ { bson_json_bson_data_t *data = &bson->bson_type_data; if (data->regex.is_legacy) { if (!data->regex.has_pattern) { - _bson_json_read_set_error (reader, "Missing \"$regex\" after \"$options\""); + _bson_json_read_set_error(reader, "Missing \"$regex\" after \"$options\""); return; } } else if (!data->regex.has_pattern) { - _bson_json_read_set_error (reader, "Missing \"pattern\" after \"options\" in regular expression"); + _bson_json_read_set_error(reader, "Missing \"pattern\" after \"options\" in regular expression"); return; } else if (!data->regex.has_options) { - _bson_json_read_set_error (reader, "Missing \"options\" after \"pattern\" in regular expression"); + _bson_json_read_set_error(reader, "Missing \"options\" after \"pattern\" in regular expression"); return; } - if (!bson_append_regex (STACK_BSON_CHILD, - bson->key, - (int) bson->key_buf.len, - (char *) bson->bson_type_buf[0].buf, - (char *) bson->bson_type_buf[1].buf)) { - _bson_json_read_set_error (reader, "Error storing regex"); + if (!bson_append_regex(STACK_BSON_CHILD, + bson->key, + (int)bson->key_buf.len, + (char *)bson->bson_type_buf[0].buf, + (char *)bson->bson_type_buf[1].buf)) { + _bson_json_read_set_error(reader, "Error storing regex"); } } static void -_bson_json_read_append_code (bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ +_bson_json_read_append_code(bson_json_reader_t *reader, /* IN */ + bson_json_reader_bson_t *bson) /* IN */ { bson_json_code_t *code_data; char *code = NULL; @@ -1418,25 +1468,25 @@ _bson_json_read_append_code (bson_json_reader_t *reader, /* IN */ code_data = &bson->code_data; - BSON_ASSERT (!code_data->in_scope); + BSON_ASSERT(!code_data->in_scope); if (!code_data->has_code) { - _bson_json_read_set_error (reader, "Missing $code after $scope"); + _bson_json_read_set_error(reader, "Missing $code after $scope"); return; } - code = (char *) code_data->code_buf.buf; + code = (char *)code_data->code_buf.buf; if (code_data->has_scope) { - scope = STACK_BSON (1); + scope = STACK_BSON(1); } /* creates BSON "code" elem, or "code with scope" if scope is not NULL */ - r = bson_append_code_with_scope ( - STACK_BSON_CHILD, (const char *) code_data->key_buf.buf, (int) code_data->key_buf.len, code, scope); + r = bson_append_code_with_scope( + STACK_BSON_CHILD, (const char *)code_data->key_buf.buf, (int)code_data->key_buf.len, code, scope); if (!r) { - _bson_json_read_set_error (reader, "Error storing Javascript code"); + _bson_json_read_set_error(reader, "Error storing Javascript code"); } /* keep the buffer but truncate it */ @@ -1446,8 +1496,8 @@ _bson_json_read_append_code (bson_json_reader_t *reader, /* IN */ static void -_bson_json_read_append_dbpointer (bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ +_bson_json_read_append_dbpointer(bson_json_reader_t *reader, /* IN */ + bson_json_reader_bson_t *bson) /* IN */ { bson_t *db_pointer; bson_iter_t iter; @@ -1455,112 +1505,112 @@ _bson_json_read_append_dbpointer (bson_json_reader_t *reader, /* IN */ const bson_oid_t *oid = NULL; bool r; - BSON_ASSERT (reader->bson.dbpointer_key.buf); + BSON_ASSERT(reader->bson.dbpointer_key.buf); - db_pointer = STACK_BSON (1); - if (!bson_iter_init (&iter, db_pointer)) { - _bson_json_read_set_error (reader, "Error storing DBPointer"); + db_pointer = STACK_BSON(1); + if (!bson_iter_init(&iter, db_pointer)) { + _bson_json_read_set_error(reader, "Error storing DBPointer"); return; } - while (bson_iter_next (&iter)) { - if (!strcmp (bson_iter_key (&iter), "$id")) { - if (!BSON_ITER_HOLDS_OID (&iter)) { - _bson_json_read_set_error (reader, "$dbPointer.$id must be like {\"$oid\": ...\"}"); + while (bson_iter_next(&iter)) { + if (!strcmp(bson_iter_key(&iter), "$id")) { + if (!BSON_ITER_HOLDS_OID(&iter)) { + _bson_json_read_set_error(reader, "$dbPointer.$id must be like {\"$oid\": ...\"}"); return; } - oid = bson_iter_oid (&iter); - } else if (!strcmp (bson_iter_key (&iter), "$ref")) { - if (!BSON_ITER_HOLDS_UTF8 (&iter)) { - _bson_json_read_set_error (reader, "$dbPointer.$ref must be a string like \"db.collection\""); + oid = bson_iter_oid(&iter); + } else if (!strcmp(bson_iter_key(&iter), "$ref")) { + if (!BSON_ITER_HOLDS_UTF8(&iter)) { + _bson_json_read_set_error(reader, "$dbPointer.$ref must be a string like \"db.collection\""); return; } - ns = bson_iter_utf8 (&iter, NULL); + ns = bson_iter_utf8(&iter, NULL); } else { - _bson_json_read_set_error (reader, "$dbPointer contains invalid key: \"%s\"", bson_iter_key (&iter)); + _bson_json_read_set_error(reader, "$dbPointer contains invalid key: \"%s\"", bson_iter_key(&iter)); return; } } if (!oid || !ns) { - _bson_json_read_set_error (reader, "$dbPointer requires both $id and $ref"); + _bson_json_read_set_error(reader, "$dbPointer requires both $id and $ref"); return; } - r = bson_append_dbpointer ( - STACK_BSON_CHILD, (char *) reader->bson.dbpointer_key.buf, (int) reader->bson.dbpointer_key.len, ns, oid); + r = bson_append_dbpointer( + STACK_BSON_CHILD, (char *)reader->bson.dbpointer_key.buf, (int)reader->bson.dbpointer_key.len, ns, oid); if (!r) { - _bson_json_read_set_error (reader, "Error storing DBPointer"); + _bson_json_read_set_error(reader, "Error storing DBPointer"); } } static void -_bson_json_read_append_oid (bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ +_bson_json_read_append_oid(bson_json_reader_t *reader, /* IN */ + bson_json_reader_bson_t *bson) /* IN */ { - if (!bson_append_oid (STACK_BSON_CHILD, bson->key, (int) bson->key_buf.len, &bson->bson_type_data.oid.oid)) { - _bson_json_read_set_error (reader, "Error storing ObjectId"); + if (!bson_append_oid(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len, &bson->bson_type_data.oid.oid)) { + _bson_json_read_set_error(reader, "Error storing ObjectId"); } } static void -_bson_json_read_append_date_time (bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ +_bson_json_read_append_date_time(bson_json_reader_t *reader, /* IN */ + bson_json_reader_bson_t *bson) /* IN */ { - if (!bson_append_date_time (STACK_BSON_CHILD, bson->key, (int) bson->key_buf.len, bson->bson_type_data.date.date)) { - _bson_json_read_set_error (reader, "Error storing datetime"); + if (!bson_append_date_time(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len, bson->bson_type_data.date.date)) { + _bson_json_read_set_error(reader, "Error storing datetime"); } } static void -_bson_json_read_append_timestamp (bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ +_bson_json_read_append_timestamp(bson_json_reader_t *reader, /* IN */ + bson_json_reader_bson_t *bson) /* IN */ { if (!bson->bson_type_data.timestamp.has_t) { - _bson_json_read_set_error (reader, "Missing t after $timestamp in BSON_TYPE_TIMESTAMP"); + _bson_json_read_set_error(reader, "Missing t after $timestamp in BSON_TYPE_TIMESTAMP"); return; } else if (!bson->bson_type_data.timestamp.has_i) { - _bson_json_read_set_error (reader, "Missing i after $timestamp in BSON_TYPE_TIMESTAMP"); + _bson_json_read_set_error(reader, "Missing i after $timestamp in BSON_TYPE_TIMESTAMP"); return; } - bson_append_timestamp (STACK_BSON_CHILD, - bson->key, - (int) bson->key_buf.len, - bson->bson_type_data.timestamp.t, - bson->bson_type_data.timestamp.i); + bson_append_timestamp(STACK_BSON_CHILD, + bson->key, + (int)bson->key_buf.len, + bson->bson_type_data.timestamp.t, + bson->bson_type_data.timestamp.i); } static void -_bad_extended_json (bson_json_reader_t *reader) +_bad_extended_json(bson_json_reader_t *reader) { - _bson_json_read_corrupt (reader, "Invalid MongoDB extended JSON"); + _bson_json_read_corrupt(reader, "Invalid MongoDB extended JSON"); } static void -_bson_json_read_end_map (bson_json_reader_t *reader) /* IN */ +_bson_json_read_end_map(bson_json_reader_t *reader) /* IN */ { bson_json_reader_bson_t *bson = &reader->bson; bool r = true; if (bson->read_state == BSON_JSON_IN_START_MAP) { bson->read_state = BSON_JSON_REGULAR; - STACK_PUSH_DOC ( - bson_append_document_begin (STACK_BSON_PARENT, bson->key, (int) bson->key_buf.len, STACK_BSON_CHILD)); + STACK_PUSH_DOC( + bson_append_document_begin(STACK_BSON_PARENT, bson->key, (int)bson->key_buf.len, STACK_BSON_CHILD)); } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_SCOPE_STARTMAP) { bson->read_state = BSON_JSON_REGULAR; STACK_PUSH_SCOPE; } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_DBPOINTER_STARTMAP) { /* we've read last "}" in "{$dbPointer: {$id: ..., $ref: ...}}" */ - _bson_json_read_append_dbpointer (reader, bson); + _bson_json_read_append_dbpointer(reader, bson); bson->read_state = BSON_JSON_REGULAR; return; } @@ -1568,57 +1618,55 @@ _bson_json_read_end_map (bson_json_reader_t *reader) /* IN */ if (bson->read_state == BSON_JSON_IN_BSON_TYPE) { if (!bson->key) { /* invalid, like {$numberLong: "1"} at the document top level */ - _bad_extended_json (reader); + _bad_extended_json(reader); return; } bson->read_state = BSON_JSON_REGULAR; switch (bson->bson_type) { case BSON_TYPE_REGEX: - _bson_json_read_append_regex (reader, bson); + _bson_json_read_append_regex(reader, bson); break; case BSON_TYPE_CODE: case BSON_TYPE_CODEWSCOPE: /* we've read the closing "}" in "{$code: ..., $scope: ...}" */ - _bson_json_read_append_code (reader, bson); + _bson_json_read_append_code(reader, bson); break; case BSON_TYPE_OID: - _bson_json_read_append_oid (reader, bson); + _bson_json_read_append_oid(reader, bson); break; case BSON_TYPE_BINARY: - _bson_json_read_append_binary (reader, bson); + _bson_json_read_append_binary(reader, bson); break; case BSON_TYPE_DATE_TIME: - _bson_json_read_append_date_time (reader, bson); + _bson_json_read_append_date_time(reader, bson); break; case BSON_TYPE_UNDEFINED: - r = bson_append_undefined (STACK_BSON_CHILD, bson->key, (int) bson->key_buf.len); + r = bson_append_undefined(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len); break; case BSON_TYPE_MINKEY: - r = bson_append_minkey (STACK_BSON_CHILD, bson->key, (int) bson->key_buf.len); + r = bson_append_minkey(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len); break; case BSON_TYPE_MAXKEY: - r = bson_append_maxkey (STACK_BSON_CHILD, bson->key, (int) bson->key_buf.len); + r = bson_append_maxkey(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len); break; case BSON_TYPE_INT32: - r = bson_append_int32 ( - STACK_BSON_CHILD, bson->key, (int) bson->key_buf.len, bson->bson_type_data.v_int32.value); + r = bson_append_int32(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len, bson->bson_type_data.v_int32.value); break; case BSON_TYPE_INT64: - r = bson_append_int64 ( - STACK_BSON_CHILD, bson->key, (int) bson->key_buf.len, bson->bson_type_data.v_int64.value); + r = bson_append_int64(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len, bson->bson_type_data.v_int64.value); break; case BSON_TYPE_DOUBLE: - r = bson_append_double ( - STACK_BSON_CHILD, bson->key, (int) bson->key_buf.len, bson->bson_type_data.v_double.value); + r = bson_append_double( + STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len, bson->bson_type_data.v_double.value); break; case BSON_TYPE_DECIMAL128: - r = bson_append_decimal128 ( - STACK_BSON_CHILD, bson->key, (int) bson->key_buf.len, &bson->bson_type_data.v_decimal128.value); + r = bson_append_decimal128( + STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len, &bson->bson_type_data.v_decimal128.value); break; case BSON_TYPE_DBPOINTER: /* shouldn't set type to DBPointer unless inside $dbPointer: {...} */ - _bson_json_read_set_error (reader, "Internal error: shouldn't be in state BSON_TYPE_DBPOINTER"); + _bson_json_read_set_error(reader, "Internal error: shouldn't be in state BSON_TYPE_DBPOINTER"); break; case BSON_TYPE_SYMBOL: break; @@ -1630,41 +1678,41 @@ _bson_json_read_end_map (bson_json_reader_t *reader) /* IN */ case BSON_TYPE_NULL: case BSON_TYPE_TIMESTAMP: default: - _bson_json_read_set_error ( - reader, "Internal error: can't parse JSON wrapper for type \"%s\"", _bson_json_type_name (bson->bson_type)); + _bson_json_read_set_error( + reader, "Internal error: can't parse JSON wrapper for type \"%s\"", _bson_json_type_name(bson->bson_type)); break; } if (!r) { - _bson_json_read_set_error (reader, "Cannot append value at end of JSON object for key %s", bson->key); + _bson_json_read_set_error(reader, "Cannot append value at end of JSON object for key %s", bson->key); } } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES) { if (!bson->key) { - _bad_extended_json (reader); + _bad_extended_json(reader); return; } bson->read_state = BSON_JSON_IN_BSON_TYPE_TIMESTAMP_ENDMAP; - _bson_json_read_append_timestamp (reader, bson); + _bson_json_read_append_timestamp(reader, bson); return; } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_REGEX_VALUES) { if (!bson->key) { - _bad_extended_json (reader); + _bad_extended_json(reader); return; } bson->read_state = BSON_JSON_IN_BSON_TYPE_REGEX_ENDMAP; - _bson_json_read_append_regex (reader, bson); + _bson_json_read_append_regex(reader, bson); return; } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_BINARY_VALUES) { if (!bson->key) { - _bad_extended_json (reader); + _bad_extended_json(reader); return; } bson->read_state = BSON_JSON_IN_BSON_TYPE_BINARY_ENDMAP; - _bson_json_read_append_binary (reader, bson); + _bson_json_read_append_binary(reader, bson); return; } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_ENDMAP) { bson->read_state = BSON_JSON_REGULAR; @@ -1674,13 +1722,13 @@ _bson_json_read_end_map (bson_json_reader_t *reader) /* IN */ bson->read_state = BSON_JSON_REGULAR; } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_DATE_NUMBERLONG) { if (!bson->key) { - _bad_extended_json (reader); + _bad_extended_json(reader); return; } bson->read_state = BSON_JSON_IN_BSON_TYPE_DATE_ENDMAP; - _bson_json_read_append_date_time (reader, bson); + _bson_json_read_append_date_time(reader, bson); return; } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_DATE_ENDMAP) { bson->read_state = BSON_JSON_REGULAR; @@ -1693,7 +1741,7 @@ _bson_json_read_end_map (bson_json_reader_t *reader) /* IN */ bson->read_state = BSON_JSON_IN_BSON_TYPE_DBPOINTER_STARTMAP; STACK_POP_DBPOINTER; } else { - STACK_POP_DOC (bson_append_document_end (STACK_BSON_PARENT, STACK_BSON_CHILD)); + STACK_POP_DOC(bson_append_document_end(STACK_BSON_PARENT, STACK_BSON_CHILD)); } if (bson->n == -1) { @@ -1701,55 +1749,55 @@ _bson_json_read_end_map (bson_json_reader_t *reader) /* IN */ } } else if (bson->read_state == BSON_JSON_IN_SCOPE) { /* empty $scope */ - BSON_ASSERT (bson->code_data.has_scope); + BSON_ASSERT(bson->code_data.has_scope); STACK_PUSH_SCOPE; STACK_POP_SCOPE; bson->read_state = BSON_JSON_IN_BSON_TYPE; bson->bson_type = BSON_TYPE_CODE; } else if (bson->read_state == BSON_JSON_IN_DBPOINTER) { /* empty $dbPointer??? */ - _bson_json_read_set_error (reader, "Empty $dbPointer"); + _bson_json_read_set_error(reader, "Empty $dbPointer"); } else { - _bson_json_read_set_error (reader, "Invalid state \"%s\"", read_state_names[bson->read_state]); + _bson_json_read_set_error(reader, "Invalid state \"%s\"", read_state_names[bson->read_state]); } } static void -_bson_json_read_start_array (bson_json_reader_t *reader) /* IN */ +_bson_json_read_start_array(bson_json_reader_t *reader) /* IN */ { const char *key; size_t len; bson_json_reader_bson_t *bson = &reader->bson; if (bson->read_state != BSON_JSON_REGULAR) { - _bson_json_read_set_error (reader, "Invalid read of \"[\" in state \"%s\"", read_state_names[bson->read_state]); + _bson_json_read_set_error(reader, "Invalid read of \"[\" in state \"%s\"", read_state_names[bson->read_state]); return; } if (bson->n == -1) { - STACK_PUSH_ARRAY (_noop ()); + STACK_PUSH_ARRAY(_noop()); } else { - _bson_json_read_fixup_key (bson); + _bson_json_read_fixup_key(bson); key = bson->key; len = bson->key_buf.len; - STACK_PUSH_ARRAY (bson_append_array_begin (STACK_BSON_PARENT, key, (int) len, STACK_BSON_CHILD)); + STACK_PUSH_ARRAY(bson_append_array_unsafe_begin(STACK_BSON_PARENT, key, (int)len, STACK_BSON_CHILD)); } } static void -_bson_json_read_end_array (bson_json_reader_t *reader) /* IN */ +_bson_json_read_end_array(bson_json_reader_t *reader) /* IN */ { bson_json_reader_bson_t *bson = &reader->bson; if (bson->read_state != BSON_JSON_REGULAR) { - _bson_json_read_set_error (reader, "Invalid read of \"]\" in state \"%s\"", read_state_names[bson->read_state]); + _bson_json_read_set_error(reader, "Invalid read of \"]\" in state \"%s\"", read_state_names[bson->read_state]); return; } - STACK_POP_ARRAY (bson_append_array_end (STACK_BSON_PARENT, STACK_BSON_CHILD)); + STACK_POP_ARRAY(bson_append_array_end(STACK_BSON_PARENT, STACK_BSON_CHILD)); if (bson->n == -1) { bson->read_state = BSON_JSON_DONE; } @@ -1759,7 +1807,7 @@ _bson_json_read_end_array (bson_json_reader_t *reader) /* IN */ /* put unescaped text in reader->bson.unescaped, or set reader->error. * json_text has length len and it is not null-terminated. */ static bool -_bson_json_unescape (bson_json_reader_t *reader, struct jsonsl_state_st *state, const char *json_text, ssize_t len) +_bson_json_unescape(bson_json_reader_t *reader, struct jsonsl_state_st *state, const char *json_text, ssize_t len) { bson_json_reader_bson_t *reader_bson; jsonsl_error_t err; @@ -1767,19 +1815,19 @@ _bson_json_unescape (bson_json_reader_t *reader, struct jsonsl_state_st *state, reader_bson = &reader->bson; /* add 1 for NULL */ - _bson_json_buf_ensure (&reader_bson->unescaped, (size_t) len + 1); + _bson_json_buf_ensure(&reader_bson->unescaped, (size_t)len + 1); /* length of unescaped str is always <= len */ reader_bson->unescaped.len = - jsonsl_util_unescape (json_text, (char *) reader_bson->unescaped.buf, (size_t) len, NULL, &err); + jsonsl_util_unescape(json_text, (char *)reader_bson->unescaped.buf, (size_t)len, NULL, &err); if (err != JSONSL_ERROR_SUCCESS) { - bson_set_error (reader->error, - BSON_ERROR_JSON, - BSON_JSON_ERROR_READ_CORRUPT_JS, - "error near position %d: \"%s\"", - (int) state->pos_begin, - jsonsl_strerror (err)); + bson_set_error(reader->error, + BSON_ERROR_JSON, + BSON_JSON_ERROR_READ_CORRUPT_JS, + "error near position %d: \"%s\"", + (int)state->pos_begin, + jsonsl_strerror(err)); return false; } @@ -1791,45 +1839,45 @@ _bson_json_unescape (bson_json_reader_t *reader, struct jsonsl_state_st *state, /* read the buffered JSON plus new data, and fill out @len with its length */ static const char * -_get_json_text (jsonsl_t json, /* IN */ - struct jsonsl_state_st *state, /* IN */ - const char *buf /* IN */, - ssize_t *len /* OUT */) +_get_json_text(jsonsl_t json, /* IN */ + struct jsonsl_state_st *state, /* IN */ + const char *buf /* IN */, + ssize_t *len /* OUT */) { bson_json_reader_t *reader; ssize_t bytes_available; - reader = (bson_json_reader_t *) json->data; + reader = (bson_json_reader_t *)json->data; - BSON_ASSERT (state->pos_cur > state->pos_begin); + BSON_ASSERT(state->pos_cur > state->pos_begin); - *len = (ssize_t) (state->pos_cur - state->pos_begin); + *len = (ssize_t)(state->pos_cur - state->pos_begin); bytes_available = buf - json->base; if (*len <= bytes_available) { /* read directly from stream, not from saved JSON */ - return buf - (size_t) *len; + return buf - (size_t)*len; } else { /* combine saved text with new data from the jsonsl_t */ ssize_t append = buf - json->base; if (append > 0) { - _bson_json_buf_append (&reader->tok_accumulator, buf - append, (size_t) append); + _bson_json_buf_append(&reader->tok_accumulator, buf - append, (size_t)append); } - return (const char *) reader->tok_accumulator.buf; + return (const char *)reader->tok_accumulator.buf; } } static void -_push_callback (jsonsl_t json, jsonsl_action_t action, struct jsonsl_state_st *state, const char *buf) +_push_callback(jsonsl_t json, jsonsl_action_t action, struct jsonsl_state_st *state, const char *buf) { - bson_json_reader_t *reader = (bson_json_reader_t *) json->data; + bson_json_reader_t *reader = (bson_json_reader_t *)json->data; - BSON_UNUSED (action); - BSON_UNUSED (buf); + BSON_UNUSED(action); + BSON_UNUSED(buf); switch (state->type) { case JSONSL_T_STRING: @@ -1839,10 +1887,10 @@ _push_callback (jsonsl_t json, jsonsl_action_t action, struct jsonsl_state_st *s reader->json_text_pos = state->pos_begin; break; case JSONSL_T_OBJECT: - _bson_json_read_start_map (reader); + _bson_json_read_start_map(reader); break; case JSONSL_T_LIST: - _bson_json_read_start_array (reader); + _bson_json_read_start_array(reader); break; default: break; @@ -1851,7 +1899,7 @@ _push_callback (jsonsl_t json, jsonsl_action_t action, struct jsonsl_state_st *s static void -_pop_callback (jsonsl_t json, jsonsl_action_t action, struct jsonsl_state_st *state, const char *buf) +_pop_callback(jsonsl_t json, jsonsl_action_t action, struct jsonsl_state_st *state, const char *buf) { bson_json_reader_t *reader; bson_json_reader_bson_t *reader_bson; @@ -1859,51 +1907,51 @@ _pop_callback (jsonsl_t json, jsonsl_action_t action, struct jsonsl_state_st *st double d; const char *obj_text; - BSON_UNUSED (action); + BSON_UNUSED(action); - reader = (bson_json_reader_t *) json->data; + reader = (bson_json_reader_t *)json->data; reader_bson = &reader->bson; switch (state->type) { case JSONSL_T_HKEY: case JSONSL_T_STRING: - obj_text = _get_json_text (json, state, buf, &len); - BSON_ASSERT (obj_text[0] == '"'); + obj_text = _get_json_text(json, state, buf, &len); + BSON_ASSERT(obj_text[0] == '"'); /* remove start/end quotes, replace backslash-escapes, null-terminate */ /* you'd think it would be faster to check if state->nescapes > 0 first, * but tests show no improvement */ - if (!_bson_json_unescape (reader, state, obj_text + 1, len - 1)) { + if (!_bson_json_unescape(reader, state, obj_text + 1, len - 1)) { /* reader->error is set */ - jsonsl_stop (json); + jsonsl_stop(json); break; } if (state->type == JSONSL_T_HKEY) { - _bson_json_read_map_key (reader, reader_bson->unescaped.buf, reader_bson->unescaped.len); + _bson_json_read_map_key(reader, reader_bson->unescaped.buf, reader_bson->unescaped.len); } else { - _bson_json_read_string (reader, reader_bson->unescaped.buf, reader_bson->unescaped.len); + _bson_json_read_string(reader, reader_bson->unescaped.buf, reader_bson->unescaped.len); } break; case JSONSL_T_OBJECT: - _bson_json_read_end_map (reader); + _bson_json_read_end_map(reader); break; case JSONSL_T_LIST: - _bson_json_read_end_array (reader); + _bson_json_read_end_array(reader); break; case JSONSL_T_SPECIAL: - obj_text = _get_json_text (json, state, buf, &len); + obj_text = _get_json_text(json, state, buf, &len); if (state->special_flags & JSONSL_SPECIALf_NUMNOINT) { - if (_bson_json_parse_double (reader, obj_text, (size_t) len, &d)) { - _bson_json_read_double (reader, d); + if (_bson_json_parse_double(reader, obj_text, (size_t)len, &d)) { + _bson_json_read_double(reader, d); } } else if (state->special_flags & JSONSL_SPECIALf_NUMERIC) { /* jsonsl puts the unsigned value in state->nelem */ - _bson_json_read_integer (reader, state->nelem, state->special_flags & JSONSL_SPECIALf_SIGNED ? -1 : 1); + _bson_json_read_integer(reader, state->nelem, state->special_flags & JSONSL_SPECIALf_SIGNED ? -1 : 1); } else if (state->special_flags & JSONSL_SPECIALf_BOOLEAN) { - _bson_json_read_boolean (reader, obj_text[0] == 't' ? 1 : 0); + _bson_json_read_boolean(reader, obj_text[0] == 't' ? 1 : 0); } else if (state->special_flags & JSONSL_SPECIALf_NULL) { - _bson_json_read_null (reader); + _bson_json_read_null(reader); } break; default: @@ -1916,11 +1964,11 @@ _pop_callback (jsonsl_t json, jsonsl_action_t action, struct jsonsl_state_st *st static int -_error_callback (jsonsl_t json, jsonsl_error_t err, struct jsonsl_state_st *state, char *errat) +_error_callback(jsonsl_t json, jsonsl_error_t err, struct jsonsl_state_st *state, char *errat) { - bson_json_reader_t *reader = (bson_json_reader_t *) json->data; + bson_json_reader_t *reader = (bson_json_reader_t *)json->data; - BSON_UNUSED (state); + BSON_UNUSED(state); if (err == JSONSL_ERROR_CANT_INSERT && *errat == '{') { /* start the next document */ @@ -1929,13 +1977,13 @@ _error_callback (jsonsl_t json, jsonsl_error_t err, struct jsonsl_state_st *stat return 0; } - bson_set_error (reader->error, - BSON_ERROR_JSON, - BSON_JSON_ERROR_READ_CORRUPT_JS, - "Got parse error at \"%c\", position %d: \"%s\"", - *errat, - (int) json->pos, - jsonsl_strerror (err)); + bson_set_error(reader->error, + BSON_ERROR_JSON, + BSON_JSON_ERROR_READ_CORRUPT_JS, + "Got parse error at \"%c\", position %d: \"%s\"", + *errat, + (int)json->pos, + jsonsl_strerror(err)); return 0; } @@ -1966,9 +2014,9 @@ _error_callback (jsonsl_t json, jsonsl_error_t err, struct jsonsl_state_st *stat */ int -bson_json_reader_read (bson_json_reader_t *reader, /* IN */ - bson_t *bson, /* IN */ - bson_error_t *error) /* OUT */ +bson_json_reader_read(bson_json_reader_t *reader, /* IN */ + bson_t *bson, /* IN */ + bson_error_t *error) /* OUT */ { bson_json_reader_producer_t *p; ssize_t start_pos; @@ -1978,8 +2026,8 @@ bson_json_reader_read (bson_json_reader_t *reader, /* IN */ bson_error_t error_tmp; int ret = 0; - BSON_ASSERT (reader); - BSON_ASSERT (bson); + BSON_ASSERT(reader); + BSON_ASSERT(bson); p = &reader->producer; @@ -1987,7 +2035,7 @@ bson_json_reader_read (bson_json_reader_t *reader, /* IN */ reader->bson.n = -1; reader->bson.read_state = BSON_JSON_REGULAR; reader->error = error ? error : &error_tmp; - memset (reader->error, 0, sizeof (bson_error_t)); + memset(reader->error, 0, sizeof(bson_error_t)); for (;;) { start_pos = reader->json->pos; @@ -1997,12 +2045,12 @@ bson_json_reader_read (bson_json_reader_t *reader, /* IN */ r = p->bytes_read; } else { /* read a chunk of bytes by executing the callback */ - r = p->cb (p->data, p->buf, p->buf_size); + r = p->cb(p->data, p->buf, p->buf_size); } if (r < 0) { if (error) { - bson_set_error (error, BSON_ERROR_JSON, BSON_JSON_ERROR_READ_CB_FAILURE, "reader cb failed"); + bson_set_error(error, BSON_ERROR_JSON, BSON_JSON_ERROR_READ_CB_FAILURE, "reader cb failed"); } ret = -1; goto cleanup; @@ -2010,17 +2058,17 @@ bson_json_reader_read (bson_json_reader_t *reader, /* IN */ break; } else { ret = 1; - p->bytes_read = (size_t) r; + p->bytes_read = (size_t)r; - jsonsl_feed (reader->json, (const jsonsl_char_t *) p->buf, (size_t) r); + jsonsl_feed(reader->json, (const jsonsl_char_t *)p->buf, (size_t)r); if (reader->should_reset) { /* end of a document */ - jsonsl_reset (reader->json); + jsonsl_reset(reader->json); reader->should_reset = false; /* advance past already-parsed data */ - memmove (p->buf, p->buf + reader->advance, r - reader->advance); + memmove(p->buf, p->buf + reader->advance, r - reader->advance); p->bytes_read -= reader->advance; ret = 1; goto cleanup; @@ -2033,13 +2081,13 @@ bson_json_reader_read (bson_json_reader_t *reader, /* IN */ /* accumulate a key or string value */ if (reader->json_text_pos != -1) { - 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 (mlib_cmp(reader->json_text_pos, <, reader->json->pos)) { + BSON_ASSERT(mlib_in_range(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); - _bson_json_buf_append (&reader->tok_accumulator, p->buf + buf_offset, (size_t) accum); + buf_offset = AT_LEAST_0(reader->json_text_pos - start_pos); + _bson_json_buf_append(&reader->tok_accumulator, p->buf + buf_offset, (size_t)accum); } } @@ -2050,7 +2098,7 @@ bson_json_reader_read (bson_json_reader_t *reader, /* IN */ cleanup: if (ret == 1 && reader->bson.read_state != BSON_JSON_DONE) { /* data ended in the middle */ - _bson_json_read_corrupt (reader, "%s", "Incomplete JSON"); + _bson_json_read_corrupt(reader, "%s", "Incomplete JSON"); return -1; } @@ -2059,25 +2107,25 @@ bson_json_reader_read (bson_json_reader_t *reader, /* IN */ bson_json_reader_t * -bson_json_reader_new (void *data, /* IN */ - bson_json_reader_cb cb, /* IN */ - bson_json_destroy_cb dcb, /* IN */ - bool allow_multiple, /* unused */ - size_t buf_size) /* IN */ +bson_json_reader_new(void *data, /* IN */ + bson_json_reader_cb cb, /* IN */ + bson_json_destroy_cb dcb, /* IN */ + bool allow_multiple, /* unused */ + size_t buf_size) /* IN */ { bson_json_reader_t *r; bson_json_reader_producer_t *p; - BSON_UNUSED (allow_multiple); + BSON_UNUSED(allow_multiple); - r = BSON_ALIGNED_ALLOC0 (bson_json_reader_t); - r->json = jsonsl_new (STACK_MAX); + 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; r->json->action_callback_POP = _pop_callback; r->json->data = r; r->json_text_pos = -1; - jsonsl_enable_all_callbacks (r->json); + jsonsl_enable_all_callbacks(r->json); p = &r->producer; @@ -2085,14 +2133,14 @@ bson_json_reader_new (void *data, /* IN */ p->cb = cb; p->dcb = dcb; p->buf_size = buf_size ? buf_size : BSON_JSON_DEFAULT_BUF_SIZE; - p->buf = bson_malloc (p->buf_size); + p->buf = bson_malloc(p->buf_size); return r; } void -bson_json_reader_destroy (bson_json_reader_t *reader) /* IN */ +bson_json_reader_destroy(bson_json_reader_t *reader) /* IN */ { int i; bson_json_reader_producer_t *p; @@ -2106,13 +2154,13 @@ bson_json_reader_destroy (bson_json_reader_t *reader) /* IN */ b = &reader->bson; if (reader->producer.dcb) { - reader->producer.dcb (reader->producer.data); + reader->producer.dcb(reader->producer.data); } - bson_free (p->buf); - bson_free (b->key_buf.buf); - bson_free (b->unescaped.buf); - bson_free (b->dbpointer_key.buf); + bson_free(p->buf); + bson_free(b->key_buf.buf); + bson_free(b->unescaped.buf); + bson_free(b->dbpointer_key.buf); /* destroy each bson_t initialized in parser stack frames */ for (i = 1; i < STACK_MAX; i++) { @@ -2121,25 +2169,25 @@ bson_json_reader_destroy (bson_json_reader_t *reader) /* IN */ break; } - if (FRAME_TYPE_HAS_BSON (b->stack[i].type)) { - bson_destroy (&b->stack[i].bson); + if (FRAME_TYPE_HAS_BSON(b->stack[i].type)) { + bson_destroy(&b->stack[i].bson); } } for (i = 0; i < 3; i++) { - bson_free (b->bson_type_buf[i].buf); + bson_free(b->bson_type_buf[i].buf); } - _bson_json_code_cleanup (&b->code_data); + _bson_json_code_cleanup(&b->code_data); - jsonsl_destroy (reader->json); - bson_free (reader->tok_accumulator.buf); - bson_free (reader); + jsonsl_destroy(reader->json); + bson_free(reader->tok_accumulator.buf); + bson_free(reader); } void -bson_json_opts_set_outermost_array (bson_json_opts_t *opts, bool is_outermost_array) +bson_json_opts_set_outermost_array(bson_json_opts_t *opts, bool is_outermost_array) { opts->is_outermost_array = is_outermost_array; } @@ -2153,18 +2201,18 @@ typedef struct { static ssize_t -_bson_json_data_reader_cb (void *_ctx, uint8_t *buf, size_t len) +_bson_json_data_reader_cb(void *_ctx, uint8_t *buf, size_t len) { size_t bytes; - bson_json_data_reader_t *ctx = (bson_json_data_reader_t *) _ctx; + bson_json_data_reader_t *ctx = (bson_json_data_reader_t *)_ctx; if (!ctx->data) { return -1; } - bytes = BSON_MIN (len, ctx->len - ctx->bytes_parsed); + bytes = BSON_MIN(len, ctx->len - ctx->bytes_parsed); - memcpy (buf, ctx->data + ctx->bytes_parsed, bytes); + memcpy(buf, ctx->data + ctx->bytes_parsed, bytes); ctx->bytes_parsed += bytes; @@ -2173,21 +2221,21 @@ _bson_json_data_reader_cb (void *_ctx, uint8_t *buf, size_t len) bson_json_reader_t * -bson_json_data_reader_new (bool allow_multiple, /* IN */ - size_t size) /* IN */ +bson_json_data_reader_new(bool allow_multiple, /* IN */ + size_t size) /* IN */ { - bson_json_data_reader_t *dr = bson_malloc0 (sizeof *dr); + bson_json_data_reader_t *dr = bson_malloc0(sizeof *dr); - return bson_json_reader_new (dr, &_bson_json_data_reader_cb, &bson_free, allow_multiple, size); + return bson_json_reader_new(dr, &_bson_json_data_reader_cb, &bson_free, allow_multiple, size); } void -bson_json_data_reader_ingest (bson_json_reader_t *reader, /* IN */ - const uint8_t *data, /* IN */ - size_t len) /* IN */ +bson_json_data_reader_ingest(bson_json_reader_t *reader, /* IN */ + const uint8_t *data, /* IN */ + size_t len) /* IN */ { - bson_json_data_reader_t *ctx = (bson_json_data_reader_t *) reader->producer.data; + bson_json_data_reader_t *ctx = (bson_json_data_reader_t *)reader->producer.data; ctx->data = data; ctx->len = len; @@ -2196,32 +2244,32 @@ bson_json_data_reader_ingest (bson_json_reader_t *reader, /* IN */ bson_t * -bson_new_from_json (const uint8_t *data, /* IN */ - ssize_t len, /* IN */ - bson_error_t *error) /* OUT */ +bson_new_from_json(const uint8_t *data, /* IN */ + ssize_t len, /* IN */ + bson_error_t *error) /* OUT */ { bson_json_reader_t *reader; bson_t *bson; int r; - BSON_ASSERT (data); + BSON_ASSERT(data); if (len < 0) { - len = (ssize_t) strlen ((const char *) data); + len = (ssize_t)strlen((const char *)data); } - bson = bson_new (); - reader = bson_json_data_reader_new (false, BSON_JSON_DEFAULT_BUF_SIZE); - bson_json_data_reader_ingest (reader, data, len); - r = bson_json_reader_read (reader, bson, error); - bson_json_reader_destroy (reader); + bson = bson_new(); + reader = bson_json_data_reader_new(false, BSON_JSON_DEFAULT_BUF_SIZE); + bson_json_data_reader_ingest(reader, data, len); + r = bson_json_reader_read(reader, bson, error); + bson_json_reader_destroy(reader); if (r == 0) { - bson_set_error (error, BSON_ERROR_JSON, BSON_JSON_ERROR_READ_INVALID_PARAM, "Empty JSON string"); + bson_set_error(error, BSON_ERROR_JSON, BSON_JSON_ERROR_READ_INVALID_PARAM, "Empty JSON string"); } if (r != 1) { - bson_destroy (bson); + bson_destroy(bson); return NULL; } @@ -2230,34 +2278,34 @@ bson_new_from_json (const uint8_t *data, /* IN */ bool -bson_init_from_json (bson_t *bson, /* OUT */ - const char *data, /* IN */ - ssize_t len, /* IN */ - bson_error_t *error) /* OUT */ +bson_init_from_json(bson_t *bson, /* OUT */ + const char *data, /* IN */ + ssize_t len, /* IN */ + bson_error_t *error) /* OUT */ { bson_json_reader_t *reader; int r; - BSON_ASSERT (bson); - BSON_ASSERT (data); + BSON_ASSERT(bson); + BSON_ASSERT(data); if (len < 0) { - len = strlen (data); + len = strlen(data); } - bson_init (bson); + bson_init(bson); - reader = bson_json_data_reader_new (false, BSON_JSON_DEFAULT_BUF_SIZE); - bson_json_data_reader_ingest (reader, (const uint8_t *) data, len); - r = bson_json_reader_read (reader, bson, error); - bson_json_reader_destroy (reader); + reader = bson_json_data_reader_new(false, BSON_JSON_DEFAULT_BUF_SIZE); + bson_json_data_reader_ingest(reader, (const uint8_t *)data, len); + r = bson_json_reader_read(reader, bson, error); + bson_json_reader_destroy(reader); if (r == 0) { - bson_set_error (error, BSON_ERROR_JSON, BSON_JSON_ERROR_READ_INVALID_PARAM, "Empty JSON string"); + bson_set_error(error, BSON_ERROR_JSON, BSON_JSON_ERROR_READ_INVALID_PARAM, "Empty JSON string"); } if (r != 1) { - bson_destroy (bson); + bson_destroy(bson); return false; } @@ -2266,27 +2314,27 @@ bson_init_from_json (bson_t *bson, /* OUT */ static void -_bson_json_reader_handle_fd_destroy (void *handle) /* IN */ +_bson_json_reader_handle_fd_destroy(void *handle) /* IN */ { bson_json_reader_handle_fd_t *fd = handle; if (fd) { if ((fd->fd != -1) && fd->do_close) { #ifdef _WIN32 - _close (fd->fd); + _close(fd->fd); #else - close (fd->fd); + close(fd->fd); #endif } - bson_free (fd); + bson_free(fd); } } static ssize_t -_bson_json_reader_handle_fd_read (void *handle, /* IN */ - uint8_t *buf, /* IN */ - size_t len) /* IN */ +_bson_json_reader_handle_fd_read(void *handle, /* IN */ + uint8_t *buf, /* IN */ + size_t len) /* IN */ { bson_json_reader_handle_fd_t *fd = handle; ssize_t ret = -1; @@ -2294,9 +2342,9 @@ _bson_json_reader_handle_fd_read (void *handle, /* IN */ if (fd && (fd->fd != -1)) { again: #ifdef BSON_OS_WIN32 - ret = _read (fd->fd, buf, (unsigned int) len); + ret = _read(fd->fd, buf, (unsigned int)len); #else - ret = read (fd->fd, buf, len); + ret = read(fd->fd, buf, len); #endif if ((ret == -1) && (errno == EAGAIN)) { goto again; @@ -2308,43 +2356,43 @@ _bson_json_reader_handle_fd_read (void *handle, /* IN */ bson_json_reader_t * -bson_json_reader_new_from_fd (int fd, /* IN */ - bool close_on_destroy) /* IN */ +bson_json_reader_new_from_fd(int fd, /* IN */ + bool close_on_destroy) /* IN */ { bson_json_reader_handle_fd_t *handle; - BSON_ASSERT (fd != -1); + BSON_ASSERT(fd != -1); - handle = bson_malloc0 (sizeof *handle); + handle = bson_malloc0(sizeof *handle); handle->fd = fd; handle->do_close = close_on_destroy; - return bson_json_reader_new ( + return bson_json_reader_new( handle, _bson_json_reader_handle_fd_read, _bson_json_reader_handle_fd_destroy, true, BSON_JSON_DEFAULT_BUF_SIZE); } bson_json_reader_t * -bson_json_reader_new_from_file (const char *path, /* IN */ - bson_error_t *error) /* OUT */ +bson_json_reader_new_from_file(const char *path, /* IN */ + bson_error_t *error) /* OUT */ { char errmsg_buf[BSON_ERROR_BUFFER_SIZE]; char *errmsg; int fd = -1; - BSON_ASSERT (path); + BSON_ASSERT(path); #ifdef BSON_OS_WIN32 - _sopen_s (&fd, path, (_O_RDONLY | _O_BINARY), _SH_DENYNO, _S_IREAD); + _sopen_s(&fd, path, (_O_RDONLY | _O_BINARY), _SH_DENYNO, _S_IREAD); #else - fd = open (path, O_RDONLY); + fd = open(path, O_RDONLY); #endif if (fd == -1) { - errmsg = bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf); - bson_set_error (error, BSON_ERROR_READER, BSON_ERROR_READER_BADFD, "%s", errmsg); + errmsg = bson_strerror_r(errno, errmsg_buf, sizeof errmsg_buf); + bson_set_error(error, BSON_ERROR_READER, BSON_ERROR_READER_BADFD, "%s", errmsg); return NULL; } - return bson_json_reader_new_from_fd (fd, true); + return bson_json_reader_new_from_fd(fd, true); } diff --git a/bsonjs/bson/bson-json.h b/bsonjs/bson/bson-json.h index 57f14a7..6fe8adb 100644 --- a/bsonjs/bson/bson-json.h +++ b/bsonjs/bson/bson-json.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ #define BSON_JSON_H -#include "bson.h" +#include BSON_BEGIN_DECLS @@ -57,32 +57,32 @@ typedef enum { } bson_json_mode_t; -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); +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, size_t count); -typedef void (*bson_json_destroy_cb) (void *handle); +typedef ssize_t(BSON_CALL *bson_json_reader_cb)(void *handle, uint8_t *buf, size_t count); +typedef void(BSON_CALL *bson_json_destroy_cb)(void *handle); -BSON_EXPORT (bson_json_reader_t *) -bson_json_reader_new ( +BSON_EXPORT(bson_json_reader_t *) +bson_json_reader_new( void *data, bson_json_reader_cb cb, bson_json_destroy_cb dcb, bool allow_multiple, size_t buf_size); -BSON_EXPORT (bson_json_reader_t *) -bson_json_reader_new_from_fd (int fd, bool close_on_destroy); -BSON_EXPORT (bson_json_reader_t *) -bson_json_reader_new_from_file (const char *filename, bson_error_t *error); -BSON_EXPORT (void) -bson_json_reader_destroy (bson_json_reader_t *reader); -BSON_EXPORT (int) -bson_json_reader_read (bson_json_reader_t *reader, bson_t *bson, bson_error_t *error); -BSON_EXPORT (bson_json_reader_t *) -bson_json_data_reader_new (bool allow_multiple, size_t size); -BSON_EXPORT (void) -bson_json_data_reader_ingest (bson_json_reader_t *reader, const uint8_t *data, size_t len); +BSON_EXPORT(bson_json_reader_t *) +bson_json_reader_new_from_fd(int fd, bool close_on_destroy); +BSON_EXPORT(bson_json_reader_t *) +bson_json_reader_new_from_file(const char *filename, bson_error_t *error); +BSON_EXPORT(void) +bson_json_reader_destroy(bson_json_reader_t *reader); +BSON_EXPORT(int) +bson_json_reader_read(bson_json_reader_t *reader, bson_t *bson, bson_error_t *error); +BSON_EXPORT(bson_json_reader_t *) +bson_json_data_reader_new(bool allow_multiple, size_t size); +BSON_EXPORT(void) +bson_json_data_reader_ingest(bson_json_reader_t *reader, const uint8_t *data, size_t len); BSON_END_DECLS diff --git a/bsonjs/bson/bson-keys.c b/bsonjs/bson/bson-keys.c index 9ba141c..0af74cd 100644 --- a/bsonjs/bson/bson-keys.c +++ b/bsonjs/bson/bson-keys.c @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +15,13 @@ */ -#include - #include -#include + +#include + +#include + +#include static const char *gUint32Strs[] = { @@ -122,10 +125,10 @@ static const char *gUint32Strs[] = { */ size_t -bson_uint32_to_string (uint32_t value, /* IN */ - const char **strptr, /* OUT */ - char *str, /* OUT */ - size_t size) /* IN */ +bson_uint32_to_string(uint32_t value, /* IN */ + const char **strptr, /* OUT */ + char *str, /* OUT */ + size_t size) /* IN */ { if (value < 1000) { *strptr = gUint32Strs[value]; @@ -141,5 +144,9 @@ bson_uint32_to_string (uint32_t value, /* IN */ *strptr = str; - return bson_snprintf (str, size, "%u", value); + int ret = bson_snprintf(str, size, "%u", value); + // Truncation is OK. + BSON_ASSERT(ret > 0); + BSON_ASSERT(mlib_in_range(size_t, ret)); + return (size_t)ret; } diff --git a/bsonjs/bson/bson-keys.h b/bsonjs/bson/bson-keys.h index 14f19f3..2c90b95 100644 --- a/bsonjs/bson/bson-keys.h +++ b/bsonjs/bson/bson-keys.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,15 +21,15 @@ #define BSON_KEYS_H -#include #include +#include BSON_BEGIN_DECLS -BSON_EXPORT (size_t) -bson_uint32_to_string (uint32_t value, const char **strptr, char *str, size_t size); +BSON_EXPORT(size_t) +bson_uint32_to_string(uint32_t value, const char **strptr, char *str, size_t size); BSON_END_DECLS diff --git a/bsonjs/bson/bson-md5.c b/bsonjs/bson/bson-md5.c deleted file mode 100644 index 5c736cf..0000000 --- a/bsonjs/bson/bson-md5.c +++ /dev/null @@ -1,24 +0,0 @@ -#include - -#include -#include "common-md5-private.h" - - -void -bson_md5_init (bson_md5_t *pms) -{ - mcommon_md5_init (pms); -} - - -void -bson_md5_append (bson_md5_t *pms, const uint8_t *data, uint32_t nbytes) -{ - mcommon_md5_append (pms, data, nbytes); -} - -void -bson_md5_finish (bson_md5_t *pms, uint8_t digest[16]) -{ - mcommon_md5_finish (pms, digest); -} diff --git a/bsonjs/bson/bson-md5.h b/bsonjs/bson/bson-md5.h deleted file mode 100644 index 77a1677..0000000 --- a/bsonjs/bson/bson-md5.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgement in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - L. Peter Deutsch - ghost@aladdin.com - - */ -/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ -/* - Independent implementation of MD5 (RFC 1321). - - This code implements the MD5 Algorithm defined in RFC 1321, whose - text is available at - http://www.ietf.org/rfc/rfc1321.txt - The code is derived from the text of the RFC, including the test suite - (section A.5) but excluding the rest of Appendix A. It does not include - any code or documentation that is identified in the RFC as being - copyrighted. - - The original and principal author of md5.h is L. Peter Deutsch - . Other authors are noted in the change history - that follows (in reverse chronological order): - - 2002-04-13 lpd Removed support for non-ANSI compilers; removed - references to Ghostscript; clarified derivation from RFC 1321; - now handles byte order either statically or dynamically. - 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. - 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); - added conditionalization for C++ compilation from Martin - Purschke . - 1999-05-03 lpd Original version. - */ - - -/* - * The following MD5 implementation has been modified to use types as - * specified in libbson. - */ - -#include - - -#ifndef BSON_MD5_H -#define BSON_MD5_H - - -#include - - -BSON_BEGIN_DECLS - - -typedef struct { - uint32_t count[2]; /* message length in bits, lsw first */ - uint32_t abcd[4]; /* digest buffer */ - uint8_t buf[64]; /* accumulate block */ -} bson_md5_t; - - -BSON_EXPORT (void) -bson_md5_init (bson_md5_t *pms) BSON_GNUC_DEPRECATED; -BSON_EXPORT (void) -bson_md5_append (bson_md5_t *pms, const uint8_t *data, uint32_t nbytes) BSON_GNUC_DEPRECATED; -BSON_EXPORT (void) -bson_md5_finish (bson_md5_t *pms, uint8_t digest[16]) BSON_GNUC_DEPRECATED; - - -BSON_END_DECLS - - -#endif /* BSON_MD5_H */ diff --git a/bsonjs/bson/bson-memory.h b/bsonjs/bson/bson-memory.h deleted file mode 100644 index bde15ed..0000000 --- a/bsonjs/bson/bson-memory.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2013 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 - - -#ifndef BSON_MEMORY_H -#define BSON_MEMORY_H - - -#include -#include - - -BSON_BEGIN_DECLS - - -typedef void *(*bson_realloc_func) (void *mem, size_t num_bytes, void *ctx); - - -typedef struct _bson_mem_vtable_t { - void *(*malloc) (size_t num_bytes); - void *(*calloc) (size_t n_members, size_t num_bytes); - void *(*realloc) (void *mem, size_t num_bytes); - void (*free) (void *mem); - void *(*aligned_alloc) (size_t alignment, size_t num_bytes); - void *padding[3]; -} bson_mem_vtable_t; - - -BSON_EXPORT (void) -bson_mem_set_vtable (const bson_mem_vtable_t *vtable); -BSON_EXPORT (void) -bson_mem_restore_vtable (void); -BSON_EXPORT (void *) -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); -BSON_EXPORT (void) -bson_free (void *mem); -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 - - -#endif /* BSON_MEMORY_H */ diff --git a/bsonjs/bson/bson-oid.c b/bsonjs/bson/bson-oid.c index e4171c7..5aedaf3 100644 --- a/bsonjs/bson/bson-oid.c +++ b/bsonjs/bson/bson-oid.c @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,21 @@ * limitations under the License. */ -#include +#include + +#include + +#include +#include +#include #include #include +#include +#include #include #include -#include -#include -#include - /* * This table contains an array of two character pairs for every possible @@ -74,104 +78,89 @@ BSON_MAYBE_UNUSED static const uint16_t gHexCharPairs[] = { #endif }; - void -bson_oid_init_sequence (bson_oid_t *oid, /* OUT */ - bson_context_t *context) /* IN */ +bson_oid_init(bson_oid_t *oid, /* OUT */ + bson_context_t *context) /* IN */ { - uint32_t now = (uint32_t) (time (NULL)); - - if (!context) { - context = bson_context_get_default (); - } - - now = BSON_UINT32_TO_BE (now); - memcpy (&oid->bytes[0], &now, sizeof (now)); - _bson_context_set_oid_seq64 (context, oid); -} - - -void -bson_oid_init (bson_oid_t *oid, /* OUT */ - bson_context_t *context) /* IN */ -{ - uint32_t now = (uint32_t) (time (NULL)); - - BSON_ASSERT (oid); - + BSON_ASSERT(oid); if (!context) { - context = bson_context_get_default (); + context = bson_context_get_default(); } - now = BSON_UINT32_TO_BE (now); - memcpy (&oid->bytes[0], &now, sizeof (now)); - _bson_context_set_oid_rand (context, oid); - _bson_context_set_oid_seq32 (context, oid); + const time_t now = time(NULL); + // Big-endian encode the low 32 bits of the time as the leading 32 bits of the new OID + oid->bytes[0] = (uint8_t)(now >> 24); + oid->bytes[1] = (uint8_t)(now >> 16); + oid->bytes[2] = (uint8_t)(now >> 8); + oid->bytes[3] = (uint8_t)(now >> 0); + // Add randomness + _bson_context_set_oid_rand(context, oid); + _bson_context_set_oid_seq32(context, oid); } void -bson_oid_init_from_data (bson_oid_t *oid, /* OUT */ - const uint8_t *data) /* IN */ +bson_oid_init_from_data(bson_oid_t *oid, /* OUT */ + const uint8_t *data) /* IN */ { - BSON_ASSERT (oid); - BSON_ASSERT (data); + BSON_ASSERT(oid); + BSON_ASSERT(data); - memcpy (oid, data, 12); + memcpy(oid, data, 12); } void -bson_oid_init_from_string (bson_oid_t *oid, /* OUT */ - const char *str) /* IN */ +bson_oid_init_from_string(bson_oid_t *oid, /* OUT */ + const char *str) /* IN */ { - BSON_ASSERT (oid); - BSON_ASSERT (str); + BSON_ASSERT(oid); + BSON_ASSERT(str); - bson_oid_init_from_string_unsafe (oid, str); + bson_oid_init_from_string_unsafe(oid, str); } time_t -bson_oid_get_time_t (const bson_oid_t *oid) /* IN */ +bson_oid_get_time_t(const bson_oid_t *oid) /* IN */ { - BSON_ASSERT (oid); + BSON_ASSERT(oid); - return bson_oid_get_time_t_unsafe (oid); + return bson_oid_get_time_t_unsafe(oid); } void -bson_oid_to_string (const bson_oid_t *oid, /* IN */ - char str[BSON_ENSURE_ARRAY_PARAM_SIZE (25)]) /* OUT */ +bson_oid_to_string(const bson_oid_t *oid, /* IN */ + char str[BSON_ENSURE_ARRAY_PARAM_SIZE(25)]) /* OUT */ { #if !defined(__i386__) && !defined(__x86_64__) && !defined(_M_IX86) && !defined(_M_X64) - BSON_ASSERT (oid); - BSON_ASSERT (str); - - bson_snprintf (str, - 25, - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - oid->bytes[0], - oid->bytes[1], - oid->bytes[2], - oid->bytes[3], - oid->bytes[4], - oid->bytes[5], - oid->bytes[6], - oid->bytes[7], - oid->bytes[8], - oid->bytes[9], - oid->bytes[10], - oid->bytes[11]); + BSON_ASSERT(oid); + BSON_ASSERT(str); + + bson_snprintf(str, + 25, + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + oid->bytes[0], + oid->bytes[1], + oid->bytes[2], + oid->bytes[3], + oid->bytes[4], + oid->bytes[5], + oid->bytes[6], + oid->bytes[7], + oid->bytes[8], + oid->bytes[9], + oid->bytes[10], + oid->bytes[11]); #else uint16_t *dst; - uint8_t *id = (uint8_t *) oid; + uint8_t *id = (uint8_t *)oid; - BSON_ASSERT (oid); - BSON_ASSERT (str); + BSON_ASSERT(oid); + BSON_ASSERT(str); - dst = (uint16_t *) (void *) str; + dst = (uint16_t *)(void *)str; dst[0] = gHexCharPairs[id[0]]; dst[1] = gHexCharPairs[id[1]]; dst[2] = gHexCharPairs[id[2]]; @@ -190,54 +179,54 @@ bson_oid_to_string (const bson_oid_t *oid, /* IN */ uint32_t -bson_oid_hash (const bson_oid_t *oid) /* IN */ +bson_oid_hash(const bson_oid_t *oid) /* IN */ { - BSON_ASSERT (oid); + BSON_ASSERT(oid); - return bson_oid_hash_unsafe (oid); + return bson_oid_hash_unsafe(oid); } int -bson_oid_compare (const bson_oid_t *oid1, /* IN */ - const bson_oid_t *oid2) /* IN */ +bson_oid_compare(const bson_oid_t *oid1, /* IN */ + const bson_oid_t *oid2) /* IN */ { - BSON_ASSERT (oid1); - BSON_ASSERT (oid2); + BSON_ASSERT(oid1); + BSON_ASSERT(oid2); - return bson_oid_compare_unsafe (oid1, oid2); + return bson_oid_compare_unsafe(oid1, oid2); } bool -bson_oid_equal (const bson_oid_t *oid1, /* IN */ - const bson_oid_t *oid2) /* IN */ +bson_oid_equal(const bson_oid_t *oid1, /* IN */ + const bson_oid_t *oid2) /* IN */ { - BSON_ASSERT (oid1); - BSON_ASSERT (oid2); + BSON_ASSERT(oid1); + BSON_ASSERT(oid2); - return bson_oid_equal_unsafe (oid1, oid2); + return bson_oid_equal_unsafe(oid1, oid2); } void -bson_oid_copy (const bson_oid_t *src, /* IN */ - bson_oid_t *dst) /* OUT */ +bson_oid_copy(const bson_oid_t *src, /* IN */ + bson_oid_t *dst) /* OUT */ { - BSON_ASSERT (src); - BSON_ASSERT (dst); + BSON_ASSERT(src); + BSON_ASSERT(dst); - bson_oid_copy_unsafe (src, dst); + bson_oid_copy_unsafe(src, dst); } bool -bson_oid_is_valid (const char *str, /* IN */ - size_t length) /* IN */ +bson_oid_is_valid(const char *str, /* IN */ + size_t length) /* IN */ { size_t i; - BSON_ASSERT (str); + BSON_ASSERT(str); if ((length == 25) && (str[24] == '\0')) { length = 24; diff --git a/bsonjs/bson/bson-oid.h b/bsonjs/bson/bson-oid.h index 4829e28..d63aa07 100644 --- a/bsonjs/bson/bson-oid.h +++ b/bsonjs/bson/bson-oid.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,39 +21,37 @@ #define BSON_OID_H -#include - #include -#include -#include #include +#include +#include + +#include BSON_BEGIN_DECLS -BSON_EXPORT (int) -bson_oid_compare (const bson_oid_t *oid1, const bson_oid_t *oid2); -BSON_EXPORT (void) -bson_oid_copy (const bson_oid_t *src, bson_oid_t *dst); -BSON_EXPORT (bool) -bson_oid_equal (const bson_oid_t *oid1, const bson_oid_t *oid2); -BSON_EXPORT (bool) -bson_oid_is_valid (const char *str, size_t length); -BSON_EXPORT (time_t) -bson_oid_get_time_t (const bson_oid_t *oid); -BSON_EXPORT (uint32_t) -bson_oid_hash (const bson_oid_t *oid); -BSON_EXPORT (void) -bson_oid_init (bson_oid_t *oid, bson_context_t *context); -BSON_EXPORT (void) -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_FOR (bson_oid_init); -BSON_EXPORT (void) -bson_oid_to_string (const bson_oid_t *oid, char str[25]); +BSON_EXPORT(int) +bson_oid_compare(const bson_oid_t *oid1, const bson_oid_t *oid2); +BSON_EXPORT(void) +bson_oid_copy(const bson_oid_t *src, bson_oid_t *dst); +BSON_EXPORT(bool) +bson_oid_equal(const bson_oid_t *oid1, const bson_oid_t *oid2); +BSON_EXPORT(bool) +bson_oid_is_valid(const char *str, size_t length); +BSON_EXPORT(time_t) +bson_oid_get_time_t(const bson_oid_t *oid); +BSON_EXPORT(uint32_t) +bson_oid_hash(const bson_oid_t *oid); +BSON_EXPORT(void) +bson_oid_init(bson_oid_t *oid, bson_context_t *context); +BSON_EXPORT(void) +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_to_string(const bson_oid_t *oid, char str[25]); /** @@ -70,9 +68,9 @@ bson_oid_to_string (const bson_oid_t *oid, char str[25]); * An integer > 0 if @oid1 is greater than @oid2. */ static BSON_INLINE int -bson_oid_compare_unsafe (const bson_oid_t *oid1, const bson_oid_t *oid2) +bson_oid_compare_unsafe(const bson_oid_t *oid1, const bson_oid_t *oid2) { - return memcmp (oid1, oid2, sizeof *oid1); + return memcmp(oid1, oid2, sizeof *oid1); } @@ -89,9 +87,9 @@ bson_oid_compare_unsafe (const bson_oid_t *oid1, const bson_oid_t *oid2) * Returns: true if @oid1 and @oid2 are equal; otherwise false. */ static BSON_INLINE bool -bson_oid_equal_unsafe (const bson_oid_t *oid1, const bson_oid_t *oid2) +bson_oid_equal_unsafe(const bson_oid_t *oid1, const bson_oid_t *oid2) { - return !memcmp (oid1, oid2, sizeof *oid1); + return !memcmp(oid1, oid2, sizeof *oid1); } /** @@ -108,13 +106,15 @@ bson_oid_equal_unsafe (const bson_oid_t *oid1, const bson_oid_t *oid2) * Returns: A uint32_t containing a hash code. */ static BSON_INLINE uint32_t -bson_oid_hash_unsafe (const bson_oid_t *oid) +bson_oid_hash_unsafe(const bson_oid_t *oid) { uint32_t hash = 5381; uint32_t i; for (i = 0; i < sizeof oid->bytes; i++) { + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN hash = ((hash << 5) + hash) + oid->bytes[i]; + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END } return hash; @@ -132,9 +132,9 @@ bson_oid_hash_unsafe (const bson_oid_t *oid) * function. */ static BSON_INLINE void -bson_oid_copy_unsafe (const bson_oid_t *src, bson_oid_t *dst) +bson_oid_copy_unsafe(const bson_oid_t *src, bson_oid_t *dst) { - memcpy (dst, src, sizeof *src); + memcpy(dst, src, sizeof *src); } @@ -149,7 +149,7 @@ bson_oid_copy_unsafe (const bson_oid_t *src, bson_oid_t *dst) * Returns: An integer between 0 and 15. */ static BSON_INLINE uint8_t -bson_oid_parse_hex_char (char hex) +bson_oid_parse_hex_char(char hex) { switch (hex) { case '0': @@ -208,13 +208,14 @@ bson_oid_parse_hex_char (char hex) * valid input to the function. */ static BSON_INLINE void -bson_oid_init_from_string_unsafe (bson_oid_t *oid, const char *str) +bson_oid_init_from_string_unsafe(bson_oid_t *oid, const char *str) { int i; for (i = 0; i < 12; i++) { - oid->bytes[i] = - (uint8_t) ((bson_oid_parse_hex_char (str[2 * i]) << 4) | (bson_oid_parse_hex_char (str[2 * i + 1]))); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + oid->bytes[i] = (uint8_t)((bson_oid_parse_hex_char(str[2 * i]) << 4) | (bson_oid_parse_hex_char(str[2 * i + 1]))); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END } } @@ -228,12 +229,12 @@ bson_oid_init_from_string_unsafe (bson_oid_t *oid, const char *str) * Returns: A time_t containing the UNIX timestamp of generation. */ static BSON_INLINE time_t -bson_oid_get_time_t_unsafe (const bson_oid_t *oid) +bson_oid_get_time_t_unsafe(const bson_oid_t *oid) { uint32_t t; - memcpy (&t, oid, sizeof (t)); - return BSON_UINT32_FROM_BE (t); + memcpy(&t, oid, sizeof(t)); + return BSON_UINT32_FROM_BE(t); } diff --git a/bsonjs/bson/bson-prelude.h b/bsonjs/bson/bson-prelude.h index 2469125..adbbf14 100644 --- a/bsonjs/bson/bson-prelude.h +++ b/bsonjs/bson/bson-prelude.h @@ -1,5 +1,5 @@ /* - * Copyright 2018-present MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/bsonjs/bson/bson-private.h b/bsonjs/bson/bson-private.h deleted file mode 100644 index 3b006a3..0000000 --- a/bsonjs/bson/bson-private.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2013 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 - - -#ifndef BSON_PRIVATE_H -#define BSON_PRIVATE_H - - -#include -#include -#include - - -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) -#define BEGIN_IGNORE_DEPRECATIONS \ - _Pragma ("GCC diagnostic push") _Pragma ("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -#define END_IGNORE_DEPRECATIONS _Pragma ("GCC diagnostic pop") -#elif defined(__clang__) -#define BEGIN_IGNORE_DEPRECATIONS \ - _Pragma ("clang diagnostic push") _Pragma ("clang diagnostic ignored \"-Wdeprecated-declarations\"") -#define END_IGNORE_DEPRECATIONS _Pragma ("clang diagnostic pop") -#else -#define BEGIN_IGNORE_DEPRECATIONS -#define END_IGNORE_DEPRECATIONS -#endif - - -BSON_BEGIN_DECLS - - -typedef enum { - BSON_FLAG_NONE = 0, - BSON_FLAG_INLINE = (1 << 0), - BSON_FLAG_STATIC = (1 << 1), - BSON_FLAG_RDONLY = (1 << 2), - BSON_FLAG_CHILD = (1 << 3), - BSON_FLAG_IN_CHILD = (1 << 4), - BSON_FLAG_NO_FREE = (1 << 5), -} bson_flags_t; - - -#ifdef BSON_MEMCHECK -#define BSON_INLINE_DATA_SIZE (120 - sizeof (char *)) -#else -#define BSON_INLINE_DATA_SIZE 120 -#endif - - -BSON_ALIGNED_BEGIN (128) -typedef struct { - bson_flags_t flags; - uint32_t len; -#ifdef BSON_MEMCHECK - char *canary; -#endif - uint8_t data[BSON_INLINE_DATA_SIZE]; -} bson_impl_inline_t BSON_ALIGNED_END (128); - - -BSON_STATIC_ASSERT2 (impl_inline_t, sizeof (bson_impl_inline_t) == 128); - - -BSON_ALIGNED_BEGIN (128) -typedef struct { - bson_flags_t flags; /* flags describing the bson_t */ - /* len is part of the public bson_t declaration. It is not - * exposed through an accessor function. Plus, it's redundant since - * BSON self describes the length in the first four bytes of the - * buffer. */ - uint32_t len; /* length of bson document in bytes */ - bson_t *parent; /* parent bson if a child */ - uint32_t depth; /* Subdocument depth. */ - uint8_t **buf; /* pointer to buffer pointer */ - size_t *buflen; /* pointer to buffer length */ - size_t offset; /* our offset inside *buf */ - uint8_t *alloc; /* buffer that we own. */ - size_t alloclen; /* length of buffer that we own. */ - bson_realloc_func realloc; /* our realloc implementation */ - void *realloc_func_ctx; /* context for our realloc func */ -} bson_impl_alloc_t BSON_ALIGNED_END (128); - - -BSON_STATIC_ASSERT2 (impl_alloc_t, sizeof (bson_impl_alloc_t) <= 128); - - -#define BSON_REGEX_OPTIONS_SORTED "ilmsux" - -BSON_END_DECLS - - -#endif /* BSON_PRIVATE_H */ diff --git a/bsonjs/bson/bson-reader.c b/bsonjs/bson/bson-reader.c index 84ade05..bd26930 100644 --- a/bsonjs/bson/bson-reader.c +++ b/bsonjs/bson/bson-reader.c @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,21 +14,25 @@ * limitations under the License. */ -#include "bson.h" +#include + +#include -#include #include + +#include #ifdef BSON_OS_WIN32 #include #include #endif -#include -#include +#include +#include + #include #include -#include -#include +#include +#include typedef enum { @@ -87,7 +91,7 @@ typedef struct { */ static void -_bson_reader_handle_fill_buffer (bson_reader_handle_t *reader) /* IN */ +_bson_reader_handle_fill_buffer(bson_reader_handle_t *reader) /* IN */ { ssize_t ret; @@ -95,7 +99,7 @@ _bson_reader_handle_fill_buffer (bson_reader_handle_t *reader) /* IN */ * Handle first read specially. */ if ((!reader->done) && (!reader->offset) && (!reader->end)) { - ret = reader->read_func (reader->handle, &reader->data[0], reader->len); + ret = reader->read_func(reader->handle, &reader->data[0], reader->len); if (ret <= 0) { reader->done = true; @@ -110,14 +114,14 @@ _bson_reader_handle_fill_buffer (bson_reader_handle_t *reader) /* IN */ /* * Move valid data to head. */ - memmove (&reader->data[0], &reader->data[reader->offset], reader->end - reader->offset); + memmove(&reader->data[0], &reader->data[reader->offset], reader->end - reader->offset); reader->end = reader->end - reader->offset; reader->offset = 0; /* * Read in data to fill the buffer. */ - ret = reader->read_func (reader->handle, &reader->data[reader->end], reader->len - reader->end); + ret = reader->read_func(reader->handle, &reader->data[reader->end], reader->len - reader->end); if (ret <= 0) { reader->done = true; @@ -127,8 +131,8 @@ _bson_reader_handle_fill_buffer (bson_reader_handle_t *reader) /* IN */ reader->end += ret; } - BSON_ASSERT (reader->offset == 0); - BSON_ASSERT (reader->end <= reader->len); + BSON_ASSERT(reader->offset == 0); + BSON_ASSERT(reader->end <= reader->len); } @@ -156,29 +160,29 @@ _bson_reader_handle_fill_buffer (bson_reader_handle_t *reader) /* IN */ */ bson_reader_t * -bson_reader_new_from_handle (void *handle, bson_reader_read_func_t rf, bson_reader_destroy_func_t df) +bson_reader_new_from_handle(void *handle, bson_reader_read_func_t rf, bson_reader_destroy_func_t df) { bson_reader_handle_t *real; - BSON_ASSERT (handle); - BSON_ASSERT (rf); + BSON_ASSERT(handle); + BSON_ASSERT(rf); - real = BSON_ALIGNED_ALLOC0 (bson_reader_handle_t); + real = BSON_ALIGNED_ALLOC0(bson_reader_handle_t); real->type = BSON_READER_HANDLE; - real->data = bson_malloc0 (1024); + real->data = bson_malloc0(1024); real->handle = handle; real->len = 1024; real->offset = 0; - bson_reader_set_read_func ((bson_reader_t *) real, rf); + bson_reader_set_read_func((bson_reader_t *)real, rf); if (df) { - bson_reader_set_destroy_func ((bson_reader_t *) real, df); + bson_reader_set_destroy_func((bson_reader_t *)real, df); } - _bson_reader_handle_fill_buffer (real); + _bson_reader_handle_fill_buffer(real); - return (bson_reader_t *) real; + return (bson_reader_t *)real; } @@ -200,19 +204,19 @@ bson_reader_new_from_handle (void *handle, bson_reader_read_func_t rf, bson_read */ static void -_bson_reader_handle_fd_destroy (void *handle) /* IN */ +_bson_reader_handle_fd_destroy(void *handle) /* IN */ { bson_reader_handle_fd_t *fd = handle; if (fd) { if ((fd->fd != -1) && fd->do_close) { #ifdef _WIN32 - _close (fd->fd); + _close(fd->fd); #else - close (fd->fd); + close(fd->fd); #endif } - bson_free (fd); + bson_free(fd); } } @@ -240,9 +244,9 @@ _bson_reader_handle_fd_destroy (void *handle) /* IN */ */ static ssize_t -_bson_reader_handle_fd_read (void *handle, /* IN */ - void *buf, /* IN */ - size_t len) /* IN */ +_bson_reader_handle_fd_read(void *handle, /* IN */ + void *buf, /* IN */ + size_t len) /* IN */ { bson_reader_handle_fd_t *fd = handle; ssize_t ret = -1; @@ -250,9 +254,9 @@ _bson_reader_handle_fd_read (void *handle, /* IN */ if (fd && (fd->fd != -1)) { again: #ifdef BSON_OS_WIN32 - ret = _read (fd->fd, buf, (unsigned int) len); + ret = _read(fd->fd, buf, (unsigned int)len); #else - ret = read (fd->fd, buf, len); + ret = read(fd->fd, buf, len); #endif if ((ret == -1) && (errno == EAGAIN)) { goto again; @@ -285,18 +289,18 @@ _bson_reader_handle_fd_read (void *handle, /* IN */ */ bson_reader_t * -bson_reader_new_from_fd (int fd, /* IN */ - bool close_on_destroy) /* IN */ +bson_reader_new_from_fd(int fd, /* IN */ + bool close_on_destroy) /* IN */ { bson_reader_handle_fd_t *handle; - BSON_ASSERT (fd != -1); + BSON_ASSERT(fd != -1); - handle = bson_malloc0 (sizeof *handle); + handle = bson_malloc0(sizeof *handle); handle->fd = fd; handle->do_close = close_on_destroy; - return bson_reader_new_from_handle (handle, _bson_reader_handle_fd_read, _bson_reader_handle_fd_destroy); + return bson_reader_new_from_handle(handle, _bson_reader_handle_fd_read, _bson_reader_handle_fd_destroy); } @@ -328,12 +332,12 @@ bson_reader_new_from_fd (int fd, /* IN */ */ void -bson_reader_set_read_func (bson_reader_t *reader, /* IN */ - bson_reader_read_func_t func) /* IN */ +bson_reader_set_read_func(bson_reader_t *reader, /* IN */ + bson_reader_read_func_t func) /* IN */ { - bson_reader_handle_t *real = (bson_reader_handle_t *) reader; + bson_reader_handle_t *real = (bson_reader_handle_t *)reader; - BSON_ASSERT (reader->type == BSON_READER_HANDLE); + BSON_ASSERT(reader->type == BSON_READER_HANDLE); real->read_func = func; } @@ -359,12 +363,12 @@ bson_reader_set_read_func (bson_reader_t *reader, /* IN */ */ void -bson_reader_set_destroy_func (bson_reader_t *reader, /* IN */ - bson_reader_destroy_func_t func) /* IN */ +bson_reader_set_destroy_func(bson_reader_t *reader, /* IN */ + bson_reader_destroy_func_t func) /* IN */ { - bson_reader_handle_t *real = (bson_reader_handle_t *) reader; + bson_reader_handle_t *real = (bson_reader_handle_t *)reader; - BSON_ASSERT (reader->type == BSON_READER_HANDLE); + BSON_ASSERT(reader->type == BSON_READER_HANDLE); real->destroy_func = func; } @@ -387,12 +391,12 @@ bson_reader_set_destroy_func (bson_reader_t *reader, /* IN */ */ static void -_bson_reader_handle_grow_buffer (bson_reader_handle_t *reader) /* IN */ +_bson_reader_handle_grow_buffer(bson_reader_handle_t *reader) /* IN */ { size_t size; size = reader->len * 2; - reader->data = bson_realloc (reader->data, size); + reader->data = bson_realloc(reader->data, size); reader->len = size; } @@ -414,13 +418,13 @@ _bson_reader_handle_grow_buffer (bson_reader_handle_t *reader) /* IN */ */ static off_t -_bson_reader_handle_tell (bson_reader_handle_t *reader) /* IN */ +_bson_reader_handle_tell(bson_reader_handle_t *reader) /* IN */ { off_t off; - off = (off_t) reader->bytes_read; - off -= (off_t) reader->end; - off += (off_t) reader->offset; + off = (off_t)reader->bytes_read; + off -= (off_t)reader->end; + off += (off_t)reader->offset; return off; } @@ -447,38 +451,34 @@ _bson_reader_handle_tell (bson_reader_handle_t *reader) /* IN */ */ static const bson_t * -_bson_reader_handle_read (bson_reader_handle_t *reader, /* IN */ - bool *reached_eof) /* IN */ +_bson_reader_handle_read(bson_reader_handle_t *reader, /* IN */ + bool *reached_eof) /* IN */ { - int32_t blen; - if (reached_eof) { *reached_eof = false; } while (!reader->done) { if ((reader->end - reader->offset) < 4) { - _bson_reader_handle_fill_buffer (reader); + _bson_reader_handle_fill_buffer(reader); continue; } - memcpy (&blen, &reader->data[reader->offset], sizeof blen); - blen = BSON_UINT32_FROM_LE (blen); - + const int32_t blen = mlib_read_i32le(reader->data + reader->offset); if (blen < 5) { return NULL; } - if (blen > (int32_t) (reader->end - reader->offset)) { - if (blen > (int32_t) reader->len) { - _bson_reader_handle_grow_buffer (reader); + if (blen > (int32_t)(reader->end - reader->offset)) { + if (blen > (int32_t)reader->len) { + _bson_reader_handle_grow_buffer(reader); } - _bson_reader_handle_fill_buffer (reader); + _bson_reader_handle_fill_buffer(reader); continue; } - if (!bson_init_static (&reader->inline_bson, &reader->data[reader->offset], (uint32_t) blen)) { + if (!bson_init_static(&reader->inline_bson, &reader->data[reader->offset], (uint32_t)blen)) { return NULL; } @@ -518,20 +518,20 @@ _bson_reader_handle_read (bson_reader_handle_t *reader, /* IN */ */ bson_reader_t * -bson_reader_new_from_data (const uint8_t *data, /* IN */ - size_t length) /* IN */ +bson_reader_new_from_data(const uint8_t *data, /* IN */ + size_t length) /* IN */ { bson_reader_data_t *real; - BSON_ASSERT (data); + BSON_ASSERT(data); - real = BSON_ALIGNED_ALLOC0 (bson_reader_data_t); + real = BSON_ALIGNED_ALLOC0(bson_reader_data_t); real->type = BSON_READER_DATA; real->data = data; real->length = length; real->offset = 0; - return (bson_reader_t *) real; + return (bson_reader_t *)real; } @@ -553,28 +553,24 @@ bson_reader_new_from_data (const uint8_t *data, /* IN */ */ static const bson_t * -_bson_reader_data_read (bson_reader_data_t *reader, /* IN */ - bool *reached_eof) /* IN */ +_bson_reader_data_read(bson_reader_data_t *reader, /* IN */ + bool *reached_eof) /* IN */ { - int32_t blen; - if (reached_eof) { *reached_eof = false; } if ((reader->offset + 4) < reader->length) { - memcpy (&blen, &reader->data[reader->offset], sizeof blen); - blen = BSON_UINT32_FROM_LE (blen); - + const int32_t blen = mlib_read_i32le(reader->data + reader->offset); if (blen < 5) { return NULL; } - if (blen > (int32_t) (reader->length - reader->offset)) { + if (blen > (int32_t)(reader->length - reader->offset)) { return NULL; } - if (!bson_init_static (&reader->inline_bson, &reader->data[reader->offset], (uint32_t) blen)) { + if (!bson_init_static(&reader->inline_bson, &reader->data[reader->offset], (uint32_t)blen)) { return NULL; } @@ -608,9 +604,9 @@ _bson_reader_data_read (bson_reader_data_t *reader, /* IN */ */ static off_t -_bson_reader_data_tell (bson_reader_data_t *reader) /* IN */ +_bson_reader_data_tell(bson_reader_data_t *reader) /* IN */ { - return (off_t) reader->offset; + return (off_t)reader->offset; } @@ -632,7 +628,7 @@ _bson_reader_data_tell (bson_reader_data_t *reader) /* IN */ */ void -bson_reader_destroy (bson_reader_t *reader) /* IN */ +bson_reader_destroy(bson_reader_t *reader) /* IN */ { if (!reader) { return; @@ -642,24 +638,24 @@ bson_reader_destroy (bson_reader_t *reader) /* IN */ case 0: break; case BSON_READER_HANDLE: { - bson_reader_handle_t *handle = (bson_reader_handle_t *) reader; + bson_reader_handle_t *handle = (bson_reader_handle_t *)reader; if (handle->destroy_func) { - handle->destroy_func (handle->handle); + handle->destroy_func(handle->handle); } - bson_free (handle->data); + bson_free(handle->data); } break; case BSON_READER_DATA: break; default: - fprintf (stderr, "No such reader type: %02x\n", reader->type); + fprintf(stderr, "No such reader type: %02x\n", reader->type); break; } reader->type = 0; - bson_free (reader); + bson_free(reader); } @@ -692,20 +688,20 @@ bson_reader_destroy (bson_reader_t *reader) /* IN */ */ const bson_t * -bson_reader_read (bson_reader_t *reader, /* IN */ - bool *reached_eof) /* OUT */ +bson_reader_read(bson_reader_t *reader, /* IN */ + bool *reached_eof) /* OUT */ { - BSON_ASSERT (reader); + BSON_ASSERT(reader); switch (reader->type) { case BSON_READER_HANDLE: - return _bson_reader_handle_read ((bson_reader_handle_t *) reader, reached_eof); + return _bson_reader_handle_read((bson_reader_handle_t *)reader, reached_eof); case BSON_READER_DATA: - return _bson_reader_data_read ((bson_reader_data_t *) reader, reached_eof); + return _bson_reader_data_read((bson_reader_data_t *)reader, reached_eof); default: - fprintf (stderr, "No such reader type: %02x\n", reader->type); + fprintf(stderr, "No such reader type: %02x\n", reader->type); break; } @@ -731,19 +727,19 @@ bson_reader_read (bson_reader_t *reader, /* IN */ */ off_t -bson_reader_tell (bson_reader_t *reader) /* IN */ +bson_reader_tell(bson_reader_t *reader) /* IN */ { - BSON_ASSERT (reader); + BSON_ASSERT(reader); switch (reader->type) { case BSON_READER_HANDLE: - return _bson_reader_handle_tell ((bson_reader_handle_t *) reader); + return _bson_reader_handle_tell((bson_reader_handle_t *)reader); case BSON_READER_DATA: - return _bson_reader_data_tell ((bson_reader_data_t *) reader); + return _bson_reader_data_tell((bson_reader_data_t *)reader); default: - fprintf (stderr, "No such reader type: %02x\n", reader->type); + fprintf(stderr, "No such reader type: %02x\n", reader->type); return -1; } } @@ -769,30 +765,30 @@ bson_reader_tell (bson_reader_t *reader) /* IN */ */ bson_reader_t * -bson_reader_new_from_file (const char *path, /* IN */ - bson_error_t *error) /* OUT */ +bson_reader_new_from_file(const char *path, /* IN */ + bson_error_t *error) /* OUT */ { char errmsg_buf[BSON_ERROR_BUFFER_SIZE]; char *errmsg; int fd; - BSON_ASSERT (path); + BSON_ASSERT(path); #ifdef BSON_OS_WIN32 - if (_sopen_s (&fd, path, (_O_RDONLY | _O_BINARY), _SH_DENYNO, 0) != 0) { + if (_sopen_s(&fd, path, (_O_RDONLY | _O_BINARY), _SH_DENYNO, 0) != 0) { fd = -1; } #else - fd = open (path, O_RDONLY); + fd = open(path, O_RDONLY); #endif if (fd == -1) { - errmsg = bson_strerror_r (errno, errmsg_buf, sizeof errmsg_buf); - bson_set_error (error, BSON_ERROR_READER, BSON_ERROR_READER_BADFD, "%s", errmsg); + errmsg = bson_strerror_r(errno, errmsg_buf, sizeof errmsg_buf); + bson_set_error(error, BSON_ERROR_READER, BSON_ERROR_READER_BADFD, "%s", errmsg); return NULL; } - return bson_reader_new_from_fd (fd, true); + return bson_reader_new_from_fd(fd, true); } @@ -808,12 +804,12 @@ bson_reader_new_from_file (const char *path, /* IN */ */ void -bson_reader_reset (bson_reader_t *reader) +bson_reader_reset(bson_reader_t *reader) { - bson_reader_data_t *real = (bson_reader_data_t *) reader; + bson_reader_data_t *real = (bson_reader_data_t *)reader; if (real->type != BSON_READER_DATA) { - fprintf (stderr, "Reader type cannot be reset\n"); + fprintf(stderr, "Reader type cannot be reset\n"); return; } diff --git a/bsonjs/bson/bson-reader.h b/bsonjs/bson/bson-reader.h index 827b0fe..c557573 100644 --- a/bsonjs/bson/bson-reader.h +++ b/bsonjs/bson/bson-reader.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,9 @@ #define BSON_READER_H -#include #include #include +#include BSON_BEGIN_DECLS @@ -59,9 +59,9 @@ BSON_BEGIN_DECLS *-------------------------------------------------------------------------- */ -typedef ssize_t (*bson_reader_read_func_t) (void *handle, /* IN */ - void *buf, /* IN */ - size_t count); /* IN */ +typedef ssize_t(BSON_CALL *bson_reader_read_func_t)(void *handle, /* IN */ + void *buf, /* IN */ + size_t count); /* IN */ /* @@ -84,29 +84,29 @@ typedef ssize_t (*bson_reader_read_func_t) (void *handle, /* IN */ *-------------------------------------------------------------------------- */ -typedef void (*bson_reader_destroy_func_t) (void *handle); /* IN */ - - -BSON_EXPORT (bson_reader_t *) -bson_reader_new_from_handle (void *handle, bson_reader_read_func_t rf, bson_reader_destroy_func_t df); -BSON_EXPORT (bson_reader_t *) -bson_reader_new_from_fd (int fd, bool close_on_destroy); -BSON_EXPORT (bson_reader_t *) -bson_reader_new_from_file (const char *path, bson_error_t *error); -BSON_EXPORT (bson_reader_t *) -bson_reader_new_from_data (const uint8_t *data, size_t length); -BSON_EXPORT (void) -bson_reader_destroy (bson_reader_t *reader); -BSON_EXPORT (void) -bson_reader_set_read_func (bson_reader_t *reader, bson_reader_read_func_t func); -BSON_EXPORT (void) -bson_reader_set_destroy_func (bson_reader_t *reader, bson_reader_destroy_func_t func); -BSON_EXPORT (const bson_t *) -bson_reader_read (bson_reader_t *reader, bool *reached_eof); -BSON_EXPORT (off_t) -bson_reader_tell (bson_reader_t *reader); -BSON_EXPORT (void) -bson_reader_reset (bson_reader_t *reader); +typedef void(BSON_CALL *bson_reader_destroy_func_t)(void *handle); /* IN */ + + +BSON_EXPORT(bson_reader_t *) +bson_reader_new_from_handle(void *handle, bson_reader_read_func_t rf, bson_reader_destroy_func_t df); +BSON_EXPORT(bson_reader_t *) +bson_reader_new_from_fd(int fd, bool close_on_destroy); +BSON_EXPORT(bson_reader_t *) +bson_reader_new_from_file(const char *path, bson_error_t *error); +BSON_EXPORT(bson_reader_t *) +bson_reader_new_from_data(const uint8_t *data, size_t length); +BSON_EXPORT(void) +bson_reader_destroy(bson_reader_t *reader); +BSON_EXPORT(void) +bson_reader_set_read_func(bson_reader_t *reader, bson_reader_read_func_t func); +BSON_EXPORT(void) +bson_reader_set_destroy_func(bson_reader_t *reader, bson_reader_destroy_func_t func); +BSON_EXPORT(const bson_t *) +bson_reader_read(bson_reader_t *reader, bool *reached_eof); +BSON_EXPORT(off_t) +bson_reader_tell(bson_reader_t *reader); +BSON_EXPORT(void) +bson_reader_reset(bson_reader_t *reader); BSON_END_DECLS diff --git a/bsonjs/bson/bson-string.c b/bsonjs/bson/bson-string.c index f9b564a..7d4a1db 100644 --- a/bsonjs/bson/bson-string.c +++ b/bsonjs/bson/bson-string.c @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,18 @@ */ -#include -#include +#include +#include -#include -#include -#include -#include -#include #include +#include +#include +#include + +#include + +#include +#include #ifdef BSON_HAVE_STRINGS_H #include @@ -31,290 +34,6 @@ #include #endif -/* - *-------------------------------------------------------------------------- - * - * bson_string_new -- - * - * Create a new bson_string_t. - * - * bson_string_t is a power-of-2 allocation growing string. Every - * time data is appended the next power of two size is chosen for - * the allocation. Pretty standard stuff. - * - * It is UTF-8 aware through the use of bson_string_append_unichar(). - * The proper UTF-8 character sequence will be used. - * - * Parameters: - * @str: a string to copy or NULL. - * - * Returns: - * A newly allocated bson_string_t that should be freed with - * bson_string_free(). - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bson_string_t * -bson_string_new (const char *str) /* IN */ -{ - bson_string_t *ret; - size_t len_sz; - - ret = bson_malloc0 (sizeof *ret); - if (str) { - len_sz = strlen (str); - BSON_ASSERT (len_sz <= UINT32_MAX); - ret->len = (uint32_t) len_sz; - } else { - ret->len = 0; - } - ret->alloc = ret->len + 1; - - if (!bson_is_power_of_two (ret->alloc)) { - len_sz = bson_next_power_of_two ((size_t) ret->alloc); - BSON_ASSERT (len_sz <= UINT32_MAX); - ret->alloc = (uint32_t) len_sz; - } - - BSON_ASSERT (ret->alloc >= ret->len + 1); - - ret->str = bson_malloc (ret->alloc); - - if (str) { - memcpy (ret->str, str, ret->len); - } - - ret->str[ret->len] = '\0'; - - return ret; -} - -char * -bson_string_free (bson_string_t *string, /* IN */ - bool free_segment) /* IN */ -{ - char *ret = NULL; - - if (!string) { - return NULL; - } - - if (!free_segment) { - ret = string->str; - } else { - bson_free (string->str); - } - - bson_free (string); - - return ret; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_string_append -- - * - * Append the UTF-8 string @str to @string. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_string_append (bson_string_t *string, /* IN */ - const char *str) /* IN */ -{ - uint32_t len; - size_t len_sz; - - BSON_ASSERT (string); - BSON_ASSERT (str); - - len_sz = strlen (str); - BSON_ASSERT (bson_in_range_unsigned (uint32_t, len_sz)); - len = (uint32_t) len_sz; - - if ((string->alloc - string->len - 1) < len) { - BSON_ASSERT (string->alloc <= UINT32_MAX - len); - string->alloc += len; - if (!bson_is_power_of_two (string->alloc)) { - len_sz = bson_next_power_of_two ((size_t) string->alloc); - BSON_ASSERT (len_sz <= UINT32_MAX); - string->alloc = (uint32_t) len_sz; - } - BSON_ASSERT (string->alloc >= string->len + len); - string->str = bson_realloc (string->str, string->alloc); - } - - memcpy (string->str + string->len, str, len); - string->len += len; - string->str[string->len] = '\0'; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_string_append_c -- - * - * Append the ASCII character @c to @string. - * - * Do not use this if you are working with UTF-8 sequences, - * use bson_string_append_unichar(). - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_string_append_c (bson_string_t *string, /* IN */ - char c) /* IN */ -{ - char cc[2]; - - BSON_ASSERT (string); - - if (BSON_UNLIKELY (string->alloc == (string->len + 1))) { - cc[0] = c; - cc[1] = '\0'; - bson_string_append (string, cc); - return; - } - - string->str[string->len++] = c; - string->str[string->len] = '\0'; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_string_append_unichar -- - * - * Append the bson_unichar_t @unichar to the string @string. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_string_append_unichar (bson_string_t *string, /* IN */ - bson_unichar_t unichar) /* IN */ -{ - uint32_t len; - char str[8]; - - BSON_ASSERT (string); - BSON_ASSERT (unichar); - - bson_utf8_from_unichar (unichar, str, &len); - - if (len <= 6) { - str[len] = '\0'; - bson_string_append (string, str); - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_string_append_printf -- - * - * Format a string according to @format and append it to @string. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_string_append_printf (bson_string_t *string, const char *format, ...) -{ - va_list args; - char *ret; - - BSON_ASSERT (string); - BSON_ASSERT (format); - - va_start (args, format); - ret = bson_strdupv_printf (format, args); - va_end (args); - bson_string_append (string, ret); - bson_free (ret); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_string_truncate -- - * - * Truncate the string @string to @len bytes. - * - * The underlying memory will be released via realloc() down to - * the minimum required size specified by @len. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_string_truncate (bson_string_t *string, /* IN */ - uint32_t len) /* IN */ -{ - uint32_t alloc; - - BSON_ASSERT (string); - BSON_ASSERT (len < INT_MAX); - - alloc = len + 1; - - if (alloc < 16) { - alloc = 16; - } - - if (!bson_is_power_of_two (alloc)) { - alloc = (uint32_t) bson_next_power_of_two ((size_t) alloc); - } - - string->str = bson_realloc (string->str, alloc); - string->alloc = alloc; - string->len = len; - - string->str[string->len] = '\0'; -} - /* *-------------------------------------------------------------------------- @@ -333,7 +52,7 @@ bson_string_truncate (bson_string_t *string, /* IN */ */ char * -bson_strdup (const char *str) /* IN */ +bson_strdup(const char *str) /* IN */ { long len; char *out; @@ -342,14 +61,14 @@ bson_strdup (const char *str) /* IN */ return NULL; } - len = (long) strlen (str); - out = bson_malloc (len + 1); + len = (long)strlen(str); + out = bson_malloc(len + 1); if (!out) { return NULL; } - memcpy (out, str, len + 1); + memcpy(out, str, len + 1); return out; } @@ -372,22 +91,22 @@ bson_strdup (const char *str) /* IN */ */ char * -bson_strdupv_printf (const char *format, /* IN */ - va_list args) /* IN */ +bson_strdupv_printf(const char *format, /* IN */ + va_list args) /* IN */ { va_list my_args; char *buf; int len = 32; int n; - BSON_ASSERT (format); + BSON_ASSERT(format); - buf = bson_malloc0 (len); + buf = bson_malloc0(len); while (true) { - va_copy (my_args, args); - n = bson_vsnprintf (buf, len, format, my_args); - va_end (my_args); + va_copy(my_args, args); + n = bson_vsnprintf(buf, len, format, my_args); + va_end(my_args); if (n > -1 && n < len) { return buf; @@ -399,7 +118,7 @@ bson_strdupv_printf (const char *format, /* IN */ len *= 2; } - buf = bson_realloc (buf, len); + buf = bson_realloc(buf, len); } } @@ -422,17 +141,17 @@ bson_strdupv_printf (const char *format, /* IN */ */ char * -bson_strdup_printf (const char *format, /* IN */ - ...) /* IN */ +bson_strdup_printf(const char *format, /* IN */ + ...) /* IN */ { va_list args; char *ret; - BSON_ASSERT (format); + BSON_ASSERT(format); - va_start (args, format); - ret = bson_strdupv_printf (format, args); - va_end (args); + va_start(args, format); + ret = bson_strdupv_printf(format, args); + va_end(args); return ret; } @@ -455,15 +174,15 @@ bson_strdup_printf (const char *format, /* IN */ */ char * -bson_strndup (const char *str, /* IN */ - size_t n_bytes) /* IN */ +bson_strndup(const char *str, /* IN */ + size_t n_bytes) /* IN */ { char *ret; - BSON_ASSERT (str); + BSON_ASSERT(str); - ret = bson_malloc (n_bytes + 1); - bson_strncpy (ret, str, n_bytes + 1); + ret = bson_malloc(n_bytes + 1); + bson_strncpy(ret, str, n_bytes + 1); return ret; } @@ -487,14 +206,14 @@ bson_strndup (const char *str, /* IN */ */ void -bson_strfreev (char **str) /* IN */ +bson_strfreev(char **str) /* IN */ { if (str) { for (char **ptr = str; *ptr != NULL; ++ptr) { - bson_free (*ptr); + bson_free(*ptr); } - bson_free (str); + bson_free(str); } } @@ -516,11 +235,11 @@ bson_strfreev (char **str) /* IN */ */ size_t -bson_strnlen (const char *s, /* IN */ - size_t maxlen) /* IN */ +bson_strnlen(const char *s, /* IN */ + size_t maxlen) /* IN */ { #ifdef BSON_HAVE_STRNLEN - return strnlen (s, maxlen); + return strnlen(s, maxlen); #else size_t i; @@ -555,9 +274,9 @@ bson_strnlen (const char *s, /* IN */ */ void -bson_strncpy (char *dst, /* IN */ - const char *src, /* IN */ - size_t size) /* IN */ +bson_strncpy(char *dst, /* IN */ + const char *src, /* IN */ + size_t size) /* IN */ { if (size == 0) { return; @@ -566,11 +285,11 @@ bson_strncpy (char *dst, /* IN */ /* Prefer strncpy_s for MSVC, or strlcpy, which has additional checks and only * adds one trailing \0 */ #ifdef _MSC_VER - strncpy_s (dst, size, src, _TRUNCATE); + strncpy_s(dst, size, src, _TRUNCATE); #elif defined(BSON_HAVE_STRLCPY) - strlcpy (dst, src, size); + strlcpy(dst, src, size); #else - strncpy (dst, src, size); + strncpy(dst, src, size); dst[size - 1] = '\0'; #endif } @@ -599,23 +318,23 @@ bson_strncpy (char *dst, /* IN */ */ int -bson_vsnprintf (char *str, /* IN */ - size_t size, /* IN */ - const char *format, /* IN */ - va_list ap) /* IN */ +bson_vsnprintf(char *str, /* IN */ + size_t size, /* IN */ + const char *format, /* IN */ + va_list ap) /* IN */ { #ifdef _MSC_VER int r = -1; - BSON_ASSERT (str); + BSON_ASSERT(str); if (size == 0) { return 0; } - r = _vsnprintf_s (str, size, _TRUNCATE, format, ap); + r = _vsnprintf_s(str, size, _TRUNCATE, format, ap); if (r == -1) { - r = _vscprintf (format, ap); + r = _vscprintf(format, ap); } str[size - 1] = '\0'; @@ -624,13 +343,13 @@ bson_vsnprintf (char *str, /* IN */ #else int r; - BSON_ASSERT (str); + BSON_ASSERT(str); if (size == 0) { return 0; } - r = vsnprintf (str, size, format, ap); + r = vsnprintf(str, size, format, ap); str[size - 1] = '\0'; return r; #endif @@ -660,19 +379,19 @@ bson_vsnprintf (char *str, /* IN */ */ int -bson_snprintf (char *str, /* IN */ - size_t size, /* IN */ - const char *format, /* IN */ - ...) +bson_snprintf(char *str, /* IN */ + size_t size, /* IN */ + const char *format, /* IN */ + ...) { int r; va_list ap; - BSON_ASSERT (str); + BSON_ASSERT(str); - va_start (ap, format); - r = bson_vsnprintf (str, size, format, ap); - va_end (ap); + va_start(ap, format); + r = bson_vsnprintf(str, size, format, ap); + va_end(ap); return r; } @@ -710,9 +429,9 @@ bson_snprintf (char *str, /* IN */ */ int64_t -bson_ascii_strtoll (const char *s, char **e, int base) +bson_ascii_strtoll(const char *s, char **e, int base) { - char *tok = (char *) s; + char *tok = (char *)s; char *digits_start; char c; int64_t number = 0; @@ -729,7 +448,7 @@ bson_ascii_strtoll (const char *s, char **e, int base) c = *tok; - while (bson_isspace (c)) { + while (bson_isspace(c)) { c = *++tok; } @@ -738,7 +457,7 @@ bson_ascii_strtoll (const char *s, char **e, int base) c = *++tok; } else if (c == '+') { c = *++tok; - } else if (!isdigit (c)) { + } else if (!isdigit(c)) { errno = EINVAL; return 0; } @@ -760,7 +479,7 @@ bson_ascii_strtoll (const char *s, char **e, int base) * than cutlim, otherwise fail. */ cutoff = sign == -1 ? INT64_MIN : INT64_MAX; - cutlim = (int) (cutoff % base); + cutlim = (int)(cutoff % base); cutoff /= base; if (sign == -1) { if (cutlim > 0) { @@ -773,10 +492,10 @@ bson_ascii_strtoll (const char *s, char **e, int base) digits_start = tok; while ((c = *tok)) { - if (isdigit (c)) { + if (isdigit(c)) { c -= '0'; - } else if (isalpha (c)) { - c -= isupper (c) ? 'A' - 10 : 'a' - 10; + } else if (isalpha(c)) { + c -= isupper(c) ? 'A' - 10 : 'a' - 10; } else { /* end of number string */ break; @@ -819,18 +538,18 @@ bson_ascii_strtoll (const char *s, char **e, int base) int -bson_strcasecmp (const char *s1, const char *s2) +bson_strcasecmp(const char *s1, const char *s2) { #ifdef BSON_OS_WIN32 - return _stricmp (s1, s2); + return _stricmp(s1, s2); #else - return strcasecmp (s1, s2); + return strcasecmp(s1, s2); #endif } bool -bson_isspace (int c) +bson_isspace(int c) { - return c >= -1 && c <= 255 && isspace (c); + return c >= -1 && c <= 255 && isspace(c); } diff --git a/bsonjs/bson/bson-string.h b/bsonjs/bson/bson-string.h index 3759aff..ab95600 100644 --- a/bsonjs/bson/bson-string.h +++ b/bsonjs/bson/bson-string.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,60 +21,49 @@ #define BSON_STRING_H -#include - -#include #include +#include + +#include BSON_BEGIN_DECLS +BSON_EXPORT(char *) +bson_strdup(const char *str); + +BSON_EXPORT(char *) +bson_strdup_printf(const char *format, ...) BSON_GNUC_PRINTF(1, 2); + +BSON_EXPORT(char *) +bson_strdupv_printf(const char *format, va_list args) BSON_GNUC_PRINTF(1, 0); + +BSON_EXPORT(char *) +bson_strndup(const char *str, size_t n_bytes); + +BSON_EXPORT(void) +bson_strncpy(char *dst, const char *src, size_t size); + +BSON_EXPORT(int) +bson_vsnprintf(char *str, size_t size, const char *format, va_list ap) BSON_GNUC_PRINTF(3, 0); + +BSON_EXPORT(int) +bson_snprintf(char *str, size_t size, const char *format, ...) BSON_GNUC_PRINTF(3, 4); + +BSON_EXPORT(void) +bson_strfreev(char **strv); + +BSON_EXPORT(size_t) +bson_strnlen(const char *s, size_t maxlen); + +BSON_EXPORT(int64_t) +bson_ascii_strtoll(const char *str, char **endptr, int base); + +BSON_EXPORT(int) +bson_strcasecmp(const char *s1, const char *s2); -typedef struct { - char *str; - uint32_t len; - uint32_t alloc; -} bson_string_t; - - -BSON_EXPORT (bson_string_t *) -bson_string_new (const char *str); -BSON_EXPORT (char *) -bson_string_free (bson_string_t *string, bool free_segment); -BSON_EXPORT (void) -bson_string_append (bson_string_t *string, const char *str); -BSON_EXPORT (void) -bson_string_append_c (bson_string_t *string, char str); -BSON_EXPORT (void) -bson_string_append_unichar (bson_string_t *string, bson_unichar_t unichar); -BSON_EXPORT (void) -bson_string_append_printf (bson_string_t *string, const char *format, ...) BSON_GNUC_PRINTF (2, 3); -BSON_EXPORT (void) -bson_string_truncate (bson_string_t *string, uint32_t len); -BSON_EXPORT (char *) -bson_strdup (const char *str); -BSON_EXPORT (char *) -bson_strdup_printf (const char *format, ...) BSON_GNUC_PRINTF (1, 2); -BSON_EXPORT (char *) -bson_strdupv_printf (const char *format, va_list args) BSON_GNUC_PRINTF (1, 0); -BSON_EXPORT (char *) -bson_strndup (const char *str, size_t n_bytes); -BSON_EXPORT (void) -bson_strncpy (char *dst, const char *src, size_t size); -BSON_EXPORT (int) -bson_vsnprintf (char *str, size_t size, const char *format, va_list ap) BSON_GNUC_PRINTF (3, 0); -BSON_EXPORT (int) -bson_snprintf (char *str, size_t size, const char *format, ...) BSON_GNUC_PRINTF (3, 4); -BSON_EXPORT (void) -bson_strfreev (char **strv); -BSON_EXPORT (size_t) -bson_strnlen (const char *s, size_t maxlen); -BSON_EXPORT (int64_t) -bson_ascii_strtoll (const char *str, char **endptr, int base); -BSON_EXPORT (int) -bson_strcasecmp (const char *s1, const char *s2); -BSON_EXPORT (bool) -bson_isspace (int c); +BSON_EXPORT(bool) +bson_isspace(int c); BSON_END_DECLS diff --git a/bsonjs/bson/bson-timegm-private.h b/bsonjs/bson/bson-timegm-private.h index 93d9a8e..e4f277c 100644 --- a/bsonjs/bson/bson-timegm-private.h +++ b/bsonjs/bson/bson-timegm-private.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,8 @@ #define BSON_TIMEGM_PRIVATE_H -#include -#include +#include +#include BSON_BEGIN_DECLS @@ -43,7 +43,7 @@ struct bson_tm { }; int64_t -_bson_timegm (struct bson_tm *const tmp); +_bson_timegm(struct bson_tm *const tmp); BSON_END_DECLS diff --git a/bsonjs/bson/bson-timegm.c b/bsonjs/bson/bson-timegm.c index 3753aa9..c289f63 100644 --- a/bsonjs/bson/bson-timegm.c +++ b/bsonjs/bson/bson-timegm.c @@ -10,37 +10,19 @@ ** and struct tm by A. Jesse Jiryu Davis for MongoDB, Inc. */ -#include -#include #include -#include "errno.h" -#include "string.h" -#include /* for INT64_MAX and INT64_MIN */ +#include +#include -/* Unlike 's isdigit, this also works if c < 0 | c > UCHAR_MAX. */ -#define is_digit(c) ((unsigned) (c) - '0' <= 9) +#include +#include /* for INT64_MAX and INT64_MIN */ +#include #if 2 < __GNUC__ + (96 <= __GNUC_MINOR__) -#define ATTRIBUTE_CONST __attribute__ ((const)) -#define ATTRIBUTE_PURE __attribute__ ((__pure__)) -#define ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec)) +#define ATTRIBUTE_PURE __attribute__((__pure__)) #else -#define ATTRIBUTE_CONST /* empty */ -#define ATTRIBUTE_PURE /* empty */ -#define ATTRIBUTE_FORMAT(spec) /* empty */ -#endif - -#if !defined _Noreturn && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112) -#if 2 < __GNUC__ + (8 <= __GNUC_MINOR__) -#define _Noreturn __attribute__ ((__noreturn__)) -#else -#define _Noreturn -#endif -#endif - -#if !defined(__STDC_VERSION__) && !defined restrict -#define restrict /* empty */ +#define ATTRIBUTE_PURE /* empty */ #endif #ifdef __clang__ @@ -84,91 +66,16 @@ static int64_t const time_t_max = INT64_MAX; #define DAYSPERNYEAR 365 #define DAYSPERLYEAR 366 #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) -#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY) +#define SECSPERDAY ((int_fast32_t)SECSPERHOUR * HOURSPERDAY) #define MONSPERYEAR 12 -#define TM_SUNDAY 0 -#define TM_MONDAY 1 -#define TM_TUESDAY 2 -#define TM_WEDNESDAY 3 -#define TM_THURSDAY 4 -#define TM_FRIDAY 5 -#define TM_SATURDAY 6 - -#define TM_JANUARY 0 -#define TM_FEBRUARY 1 -#define TM_MARCH 2 -#define TM_APRIL 3 -#define TM_MAY 4 -#define TM_JUNE 5 -#define TM_JULY 6 -#define TM_AUGUST 7 -#define TM_SEPTEMBER 8 -#define TM_OCTOBER 9 -#define TM_NOVEMBER 10 -#define TM_DECEMBER 11 - #define TM_YEAR_BASE 1900 #define EPOCH_YEAR 1970 -#define EPOCH_WDAY TM_THURSDAY +#define EPOCH_WDAY 4 /* TM_THURSDAY */ #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) -/* -** Since everything in isleap is modulo 400 (or a factor of 400), we know that -** isleap(y) == isleap(y % 400) -** and so -** isleap(a + b) == isleap((a + b) % 400) -** or -** isleap(a + b) == isleap(a % 400 + b % 400) -** This is true even if % means modulo rather than Fortran remainder -** (which is allowed by C89 but not C99). -** We use this to avoid addition overflow problems. -*/ - -#define isleap_sum(a, b) isleap ((a) % 400 + (b) % 400) - -#ifndef TZ_ABBR_MAX_LEN -#define TZ_ABBR_MAX_LEN 16 -#endif /* !defined TZ_ABBR_MAX_LEN */ - -#ifndef TZ_ABBR_CHAR_SET -#define TZ_ABBR_CHAR_SET "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._" -#endif /* !defined TZ_ABBR_CHAR_SET */ - -#ifndef TZ_ABBR_ERR_CHAR -#define TZ_ABBR_ERR_CHAR '_' -#endif /* !defined TZ_ABBR_ERR_CHAR */ - -#ifndef WILDABBR -/* -** Someone might make incorrect use of a time zone abbreviation: -** 1. They might reference tzname[0] before calling tzset (explicitly -** or implicitly). -** 2. They might reference tzname[1] before calling tzset (explicitly -** or implicitly). -** 3. They might reference tzname[1] after setting to a time zone -** in which Daylight Saving Time is never observed. -** 4. They might reference tzname[0] after setting to a time zone -** in which Standard Time is never observed. -** 5. They might reference tm.TM_ZONE after calling offtime. -** What's best to do in the above cases is open to debate; -** for now, we just set things up so that in any of the five cases -** WILDABBR is used. Another possibility: initialize tzname[0] to the -** string "tzname[0] used before set", and similarly for the other cases. -** And another: initialize tzname[0] to "ERA", with an explanation in the -** manual page of what this "time zone abbreviation" means (doing this so -** that tzname[0] has the "normal" length of three characters). -*/ -#define WILDABBR " " -#endif /* !defined WILDABBR */ - -#ifdef TM_ZONE -static const char wildabbr[] = WILDABBR; -static const char gmt[] = "GMT"; -#endif - struct ttinfo { /* time type information */ int_fast32_t tt_gmtoff; /* UT offset in seconds */ int tt_isdst; /* used to set tm_isdst */ @@ -201,7 +108,7 @@ struct state { int64_t ats[TZ_MAX_TIMES]; unsigned char types[TZ_MAX_TIMES]; struct ttinfo ttis[TZ_MAX_TYPES]; - char chars[BIGGEST (TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))]; + char chars[BIGGEST(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))]; struct lsinfo lsis[TZ_MAX_LEAPS]; int defaulttype; /* for early times or if no transitions */ }; @@ -214,50 +121,43 @@ struct rule { int_fast32_t r_time; /* transition time of rule */ }; -#define JULIAN_DAY 0 /* Jn - Julian day */ -#define DAY_OF_YEAR 1 /* n - day of year */ -#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */ - /* ** Prototypes for static functions. */ static void -gmtload (struct state *const sp); +gmtload(struct state *const sp); static struct bson_tm * -gmtsub (const int64_t *const timep, const int_fast32_t offset, struct bson_tm *const tmp); +gmtsub(const int64_t *const timep, const int_fast32_t offset, struct bson_tm *const tmp); static int64_t -increment_overflow (int64_t *const ip, int64_t j); +increment_overflow(int64_t *const ip, int64_t j); static int64_t -leaps_thru_end_of (const int64_t y) ATTRIBUTE_PURE; +leaps_thru_end_of(const int64_t y) ATTRIBUTE_PURE; static int64_t -increment_overflow32 (int_fast32_t *const lp, int64_t const m); +increment_overflow32(int_fast32_t *const lp, int64_t const m); static int64_t -normalize_overflow32 (int_fast32_t *const tensptr, int64_t *const unitsptr, const 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 *const tensptr, int64_t *const unitsptr, const int64_t base); +normalize_overflow(int64_t *const tensptr, int64_t *const unitsptr, const int64_t base); static int64_t -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); +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 *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); +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 *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 *const timep, +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, - const struct state *const sp, - struct bson_tm *const tmp); + int64_t *const okayp, + const int64_t do_norm_secs); +static struct bson_tm * +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 *const atmp, const struct bson_tm *const btmp); +tmcomp(const struct bson_tm *const atmp, const struct bson_tm *const btmp); static struct state gmtmem; #define gmtptr (&gmtmem) @@ -270,9 +170,9 @@ static const int mon_lengths[2][MONSPERYEAR] = {{31, 28, 31, 30, 31, 30, 31, 31, static const int year_lengths[2] = {DAYSPERNYEAR, DAYSPERLYEAR}; static void -gmtload (struct state *const sp) +gmtload(struct state *const sp) { - memset (sp, 0, sizeof (struct state)); + memset(sp, 0, sizeof(struct state)); sp->typecnt = 1; sp->charcnt = 4; sp->chars[0] = 'G'; @@ -285,23 +185,15 @@ gmtload (struct state *const sp) */ static struct bson_tm * -gmtsub (const int64_t *const timep, const int_fast32_t offset, struct bson_tm *const tmp) +gmtsub(const int64_t *const timep, const int_fast32_t offset, struct bson_tm *const tmp) { struct bson_tm *result; if (!gmt_is_set) { gmt_is_set = true; - gmtload (gmtptr); + gmtload(gmtptr); } - result = timesub (timep, offset, gmtptr, tmp); -#ifdef TM_ZONE - /* - ** Could get fancy here and deliver something such as - ** "UT+xxxx" or "UT-xxxx" if offset is non-zero, - ** but this is no time for a treasure hunt. - */ - tmp->TM_ZONE = offset ? wildabbr : gmtptr ? gmtptr->chars : gmt; -#endif /* defined TM_ZONE */ + result = timesub(timep, offset, gmtptr, tmp); return result; } @@ -311,13 +203,13 @@ gmtsub (const int64_t *const timep, const int_fast32_t offset, struct bson_tm *c */ static int64_t -leaps_thru_end_of (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); + return (y >= 0) ? (y / 4 - y / 100 + y / 400) : -(leaps_thru_end_of(-(y + 1)) + 1); } static struct bson_tm * -timesub (const int64_t *const timep, const int_fast32_t offset, const struct state *const sp, struct bson_tm *const tmp) +timesub(const int64_t *const timep, const int_fast32_t offset, const struct state *const sp, struct bson_tm *const tmp) { const struct lsinfo *lp; int64_t tdays; @@ -351,7 +243,7 @@ timesub (const int64_t *const timep, const int_fast32_t offset, const struct sta y = EPOCH_YEAR; tdays = *timep / SECSPERDAY; rem = *timep - tdays * SECSPERDAY; - while (tdays < 0 || tdays >= year_lengths[isleap (y)]) { + while (tdays < 0 || tdays >= year_lengths[isleap(y)]) { int64_t newy; int64_t tdelta; int64_t idelta; @@ -362,24 +254,24 @@ timesub (const int64_t *const timep, const int_fast32_t offset, const struct sta if (idelta == 0) idelta = (tdays < 0) ? -1 : 1; newy = y; - if (increment_overflow (&newy, idelta)) + if (increment_overflow(&newy, idelta)) return NULL; - leapdays = leaps_thru_end_of (newy - 1) - leaps_thru_end_of (y - 1); - tdays -= ((int64_t) newy - y) * DAYSPERNYEAR; + leapdays = leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1); + tdays -= ((int64_t)newy - y) * DAYSPERNYEAR; tdays -= leapdays; y = newy; } { int_fast32_t seconds; - seconds = (int_fast32_t) (tdays * SECSPERDAY); + seconds = (int_fast32_t)(tdays * SECSPERDAY); tdays = seconds / SECSPERDAY; rem += seconds - tdays * SECSPERDAY; } /* ** Given the range, we can now fearlessly cast... */ - idays = (int64_t) tdays; + idays = (int64_t)tdays; rem += offset - corr; while (rem < 0) { rem += SECSPERDAY; @@ -390,42 +282,42 @@ timesub (const int64_t *const timep, const int_fast32_t offset, const struct sta ++idays; } while (idays < 0) { - if (increment_overflow (&y, -1)) + if (increment_overflow(&y, -1)) return NULL; - idays += year_lengths[isleap (y)]; + idays += year_lengths[isleap(y)]; } - while (idays >= year_lengths[isleap (y)]) { - idays -= year_lengths[isleap (y)]; - if (increment_overflow (&y, 1)) + while (idays >= year_lengths[isleap(y)]) { + idays -= year_lengths[isleap(y)]; + if (increment_overflow(&y, 1)) return NULL; } tmp->tm_year = y; - if (increment_overflow (&tmp->tm_year, -TM_YEAR_BASE)) + if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) return NULL; tmp->tm_yday = idays; /* ** The "extra" mods below avoid overflow problems. */ tmp->tm_wday = EPOCH_WDAY + ((y - EPOCH_YEAR) % DAYSPERWEEK) * (DAYSPERNYEAR % DAYSPERWEEK) + - leaps_thru_end_of (y - 1) - leaps_thru_end_of (EPOCH_YEAR - 1) + idays; + leaps_thru_end_of(y - 1) - leaps_thru_end_of(EPOCH_YEAR - 1) + idays; tmp->tm_wday %= DAYSPERWEEK; if (tmp->tm_wday < 0) tmp->tm_wday += DAYSPERWEEK; - tmp->tm_hour = (int64_t) (rem / SECSPERHOUR); + tmp->tm_hour = (int64_t)(rem / SECSPERHOUR); rem %= SECSPERHOUR; - tmp->tm_min = (int64_t) (rem / SECSPERMIN); + tmp->tm_min = (int64_t)(rem / SECSPERMIN); /* ** A positive leap second requires a special ** representation. This uses "... ??:59:60" et seq. */ - tmp->tm_sec = (int64_t) (rem % SECSPERMIN) + hit; - ip = mon_lengths + (isleap (y) ? 1 : 0); + tmp->tm_sec = (int64_t)(rem % SECSPERMIN) + hit; + 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); + BSON_ASSERT(tmp->tm_mon < MONSPERYEAR); } - tmp->tm_mday = (int64_t) (idays + 1); + tmp->tm_mday = (int64_t)(idays + 1); tmp->tm_isdst = 0; #ifdef TM_GMTOFF tmp->TM_GMTOFF = offset; @@ -451,7 +343,7 @@ timesub (const int64_t *const timep, const int_fast32_t offset, const struct sta */ static int64_t -increment_overflow (int64_t *const ip, int64_t j) +increment_overflow(int64_t *const ip, int64_t j) { int64_t const i = *ip; @@ -468,38 +360,38 @@ increment_overflow (int64_t *const ip, int64_t j) } static int64_t -increment_overflow32 (int_fast32_t *const lp, int64_t const m) +increment_overflow32(int_fast32_t *const lp, int64_t const m) { int_fast32_t const l = *lp; if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l)) return true; - *lp += (int_fast32_t) 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) { int64_t tensdelta; tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base); *unitsptr -= tensdelta * base; - return increment_overflow (tensptr, tensdelta); + return increment_overflow(tensptr, tensdelta); } static int64_t -normalize_overflow32 (int_fast32_t *const tensptr, int64_t *const unitsptr, const int64_t base) +normalize_overflow32(int_fast32_t *const tensptr, int64_t *const unitsptr, const int64_t base) { int64_t tensdelta; tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base); *unitsptr -= tensdelta * base; - return increment_overflow32 (tensptr, tensdelta); + return increment_overflow32(tensptr, tensdelta); } static int64_t -tmcomp (const struct bson_tm *const atmp, const struct bson_tm *const btmp) +tmcomp(const struct bson_tm *const atmp, const struct bson_tm *const btmp) { int64_t result; @@ -512,11 +404,11 @@ tmcomp (const struct bson_tm *const atmp, const struct bson_tm *const btmp) } static int64_t -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) +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) { const struct state *sp; int64_t dir; @@ -533,46 +425,46 @@ time2sub (struct bson_tm *const tmp, *okayp = false; yourtm = *tmp; if (do_norm_secs) { - if (normalize_overflow (&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN)) + if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN)) return WRONG; } - if (normalize_overflow (&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) + if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) return WRONG; - if (normalize_overflow (&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) + if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) return WRONG; - y = (int_fast32_t) yourtm.tm_year; - if (normalize_overflow32 (&y, &yourtm.tm_mon, MONSPERYEAR)) + y = (int_fast32_t)yourtm.tm_year; + if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR)) return WRONG; /* ** Turn y into an actual year number for now. ** It is converted back to an offset from TM_YEAR_BASE later. */ - if (increment_overflow32 (&y, TM_YEAR_BASE)) + if (increment_overflow32(&y, TM_YEAR_BASE)) return WRONG; while (yourtm.tm_mday <= 0) { - if (increment_overflow32 (&y, -1)) + if (increment_overflow32(&y, -1)) return WRONG; li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday += year_lengths[isleap (li)]; + yourtm.tm_mday += year_lengths[isleap(li)]; } while (yourtm.tm_mday > DAYSPERLYEAR) { li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday -= year_lengths[isleap (li)]; - if (increment_overflow32 (&y, 1)) + yourtm.tm_mday -= year_lengths[isleap(li)]; + if (increment_overflow32(&y, 1)) return WRONG; } for (;;) { - i = mon_lengths[isleap (y)][yourtm.tm_mon]; + i = mon_lengths[isleap(y)][yourtm.tm_mon]; if (yourtm.tm_mday <= i) break; yourtm.tm_mday -= i; if (++yourtm.tm_mon >= MONSPERYEAR) { yourtm.tm_mon = 0; - if (increment_overflow32 (&y, 1)) + if (increment_overflow32(&y, 1)) return WRONG; } } - if (increment_overflow32 (&y, -TM_YEAR_BASE)) + if (increment_overflow32(&y, -TM_YEAR_BASE)) return WRONG; yourtm.tm_year = y; if (yourtm.tm_year != y) @@ -588,7 +480,7 @@ time2sub (struct bson_tm *const tmp, ** not in the same minute that a leap second was deleted from, ** which is a safer assumption than using 58 would be. */ - if (increment_overflow (&yourtm.tm_sec, 1 - SECSPERMIN)) + if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) return WRONG; saved_seconds = yourtm.tm_sec; yourtm.tm_sec = SECSPERMIN - 1; @@ -608,7 +500,7 @@ time2sub (struct bson_tm *const tmp, t = lo; else if (t > hi) t = hi; - if ((*funcp) (&t, offset, &mytm) == NULL) { + if ((*funcp)(&t, offset, &mytm) == NULL) { /* ** Assume that t is too extreme to be represented in ** a struct bson_tm; arrange things so that it is less @@ -616,7 +508,7 @@ time2sub (struct bson_tm *const tmp, */ dir = (t > 0) ? 1 : -1; } else - dir = tmcomp (&mytm, &yourtm); + dir = tmcomp(&mytm, &yourtm); if (dir != 0) { if (t == lo) { if (t == time_t_max) @@ -645,7 +537,7 @@ time2sub (struct bson_tm *const tmp, ** It's okay to guess wrong since the guess ** gets checked. */ - sp = (const struct state *) gmtptr; + sp = (const struct state *)gmtptr; if (sp == NULL) return WRONG; for (i = sp->typecnt - 1; i >= 0; --i) { @@ -655,9 +547,9 @@ time2sub (struct bson_tm *const tmp, if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) continue; newt = t + sp->ttis[j].tt_gmtoff - sp->ttis[i].tt_gmtoff; - if ((*funcp) (&newt, offset, &mytm) == NULL) + if ((*funcp)(&newt, offset, &mytm) == NULL) continue; - if (tmcomp (&mytm, &yourtm) != 0) + if (tmcomp(&mytm, &yourtm) != 0) continue; if (mytm.tm_isdst != yourtm.tm_isdst) continue; @@ -675,16 +567,16 @@ time2sub (struct bson_tm *const tmp, if ((newt < t) != (saved_seconds < 0)) return WRONG; t = newt; - if ((*funcp) (&t, offset, tmp)) + if ((*funcp)(&t, offset, tmp)) *okayp = true; return t; } static int64_t -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) +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) { int64_t t; @@ -693,14 +585,14 @@ time2 (struct bson_tm *const tmp, ** (in case tm_sec contains a value associated with a leap second). ** If that fails, try with normalization of seconds. */ - t = time2sub (tmp, funcp, offset, okayp, false); - return *okayp ? t : time2sub (tmp, funcp, offset, okayp, true); + t = time2sub(tmp, funcp, offset, okayp, false); + return *okayp ? t : time2sub(tmp, funcp, offset, okayp, true); } static int64_t -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) +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) { int64_t t; const struct state *sp; @@ -718,7 +610,7 @@ time1 (struct bson_tm *const tmp, } if (tmp->tm_isdst > 1) tmp->tm_isdst = 1; - t = time2 (tmp, funcp, offset, &okay); + t = time2(tmp, funcp, offset, &okay); if (okay) return t; if (tmp->tm_isdst < 0) @@ -736,7 +628,7 @@ time1 (struct bson_tm *const tmp, ** We try to divine the type they started from and adjust to the ** type they need. */ - sp = (const struct state *) gmtptr; + sp = (const struct state *)gmtptr; if (sp == NULL) return WRONG; for (i = 0; i < sp->typecnt; ++i) @@ -757,7 +649,7 @@ time1 (struct bson_tm *const tmp, continue; tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - sp->ttis[samei].tt_gmtoff; tmp->tm_isdst = !tmp->tm_isdst; - t = time2 (tmp, funcp, offset, &okay); + t = time2(tmp, funcp, offset, &okay); if (okay) return t; tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - sp->ttis[samei].tt_gmtoff; @@ -768,9 +660,9 @@ time1 (struct bson_tm *const tmp, } int64_t -_bson_timegm (struct bson_tm *const tmp) +_bson_timegm(struct bson_tm *const tmp) { if (tmp != NULL) tmp->tm_isdst = 0; - return time1 (tmp, gmtsub, 0L); + return time1(tmp, gmtsub, 0L); } diff --git a/bsonjs/bson/bson-types.h b/bsonjs/bson/bson-types.h index 3148668..9378a2b 100644 --- a/bsonjs/bson/bson-types.h +++ b/bsonjs/bson/bson-types.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,14 +20,16 @@ #ifndef BSON_TYPES_H #define BSON_TYPES_H +#include +#include +#include +#include +#include +#include -#include #include -#include -#include -#include -#include +#include BSON_BEGIN_DECLS @@ -41,9 +43,6 @@ BSON_BEGIN_DECLS * unicode characters. When iterating UTF-8 sequences, this should * be used to avoid losing the high-bits of non-ascii characters. * - * See also: - * bson_string_append_unichar() - * *-------------------------------------------------------------------------- */ @@ -106,67 +105,6 @@ typedef struct _bson_context_t bson_context_t; typedef struct _bson_json_opts_t bson_json_opts_t; -/** - * bson_t: - * - * This structure manages a buffer whose contents are a properly formatted - * BSON document. You may perform various transforms on the BSON documents. - * Additionally, it can be iterated over using bson_iter_t. - * - * See bson_iter_init() for iterating the contents of a bson_t. - * - * When building a bson_t structure using the various append functions, - * memory allocations may occur. That is performed using power of two - * allocations and realloc(). - * - * See http://bsonspec.org for the BSON document spec. - * - * This structure is meant to fit in two sequential 64-byte cachelines. - */ -#ifdef BSON_MEMCHECK -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 leak checks. */ - uint8_t padding[120 - sizeof (char *)]; -} bson_t BSON_ALIGNED_END (128); -#else -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. */ -} bson_t BSON_ALIGNED_END (128); -#endif - -/** - * BSON_INITIALIZER: - * - * This macro can be used to initialize a #bson_t structure on the stack - * without calling bson_init(). - * - * |[ - * bson_t b = BSON_INITIALIZER; - * ]| - */ -#ifdef BSON_MEMCHECK -#define BSON_INITIALIZER \ - { \ - 3, 5, bson_malloc (1), {5}, \ - } -#else -#define BSON_INITIALIZER \ - { \ - 3, 5, \ - { \ - 5 \ - } \ - } -#endif - - -BSON_STATIC_ASSERT2 (bson_t, sizeof (bson_t) == 128); - - /** * bson_oid_t: * @@ -174,11 +112,11 @@ BSON_STATIC_ASSERT2 (bson_t, sizeof (bson_t) == 128); * on http://bsonspec.org. If you would like the bson_oid_t in string form * see bson_oid_to_string() or bson_oid_to_string_r(). */ -typedef struct { +typedef struct bson_oid_t { uint8_t bytes[12]; } bson_oid_t; -BSON_STATIC_ASSERT2 (oid_t, sizeof (bson_oid_t) == 12); +BSON_STATIC_ASSERT2(oid_t, sizeof(bson_oid_t) == 12); /** * bson_decimal128_t: @@ -204,25 +142,54 @@ typedef struct { /** - * bson_validate_flags_t: + * @brief Flags and error codes for BSON validation functions. * - * This enumeration is used for validation of BSON documents. It allows - * selective control on what you wish to validate. + * Pass these flags bits to control the behavior of the `bson_validate` family + * of functions. * - * %BSON_VALIDATE_NONE: No additional validation occurs. - * %BSON_VALIDATE_UTF8: Check that strings are valid UTF-8. - * %BSON_VALIDATE_DOLLAR_KEYS: Check that keys do not start with $. - * %BSON_VALIDATE_DOT_KEYS: Check that keys do not contain a period. - * %BSON_VALIDATE_UTF8_ALLOW_NULL: Allow NUL bytes in UTF-8 text. - * %BSON_VALIDATE_EMPTY_KEYS: Prohibit zero-length field names + * Additionally, if validation fails, then the error code set on a `bson_error_t` + * will have the value corresponding to the reason that validation failed. */ typedef enum { + /** + * @brief No special validation behavior specified. + */ BSON_VALIDATE_NONE = 0, + /** + * @brief Check that all text components of the BSON data are valid UTF-8. + * + * Note that this will also cause validation to reject valid text that contains + * a null character. This can be changed by also passing + * `BSON_VALIDATE_UTF8_ALLOW_NULL` + */ BSON_VALIDATE_UTF8 = (1 << 0), + /** + * @brief Check that element keys do not begin with an ASCII dollar `$` + */ BSON_VALIDATE_DOLLAR_KEYS = (1 << 1), + /** + * @brief Check that element keys do not contain an ASCII period `.` + */ BSON_VALIDATE_DOT_KEYS = (1 << 2), + /** + * @brief If set then it is *not* an error for a UTF-8 string to contain + * embedded null characters. + * + * This has no effect unless `BSON_VALIDATE_UTF8` is also passed. + */ BSON_VALIDATE_UTF8_ALLOW_NULL = (1 << 3), + /** + * @brief Check that no element key is a zero-length empty string. + */ BSON_VALIDATE_EMPTY_KEYS = (1 << 4), + /** + * @brief This is not a flag that controls behavior, but is instead used to indicate + * that a BSON document is corrupted in some way. This is the value that will + * appear as an error code. + * + * Passing this as a flag has no effect. + */ + BSON_VALIDATE_CORRUPT = (1 << 5), } bson_validate_flags_t; @@ -274,6 +241,7 @@ typedef enum { BSON_SUBTYPE_ENCRYPTED = 0x06, BSON_SUBTYPE_COLUMN = 0x07, BSON_SUBTYPE_SENSITIVE = 0x08, + BSON_SUBTYPE_VECTOR = 0x09, BSON_SUBTYPE_USER = 0x80, } bson_subtype_t; @@ -292,7 +260,6 @@ typedef enum { *-------------------------------------------------------------------------- */ -BSON_ALIGNED_BEGIN (8) typedef struct _bson_value_t { bson_type_t value_type; int32_t padding; @@ -346,7 +313,7 @@ typedef struct _bson_value_t { } v_symbol; bson_decimal128_t v_decimal128; } value; -} bson_value_t BSON_ALIGNED_END (8); +} bson_value_t; /** @@ -360,8 +327,7 @@ typedef struct _bson_value_t { * This structure is safe to discard on the stack. No cleanup is necessary * after using it. */ -BSON_ALIGNED_BEGIN (128) -typedef struct { +typedef struct bson_iter_t { const uint8_t *raw; /* The raw buffer being iterated. */ uint32_t len; /* The length of raw. */ uint32_t off; /* The offset within the buffer. */ @@ -374,7 +340,7 @@ typedef struct { uint32_t next_off; /* The offset of the next field. */ uint32_t err_off; /* The offset of the error. */ bson_value_t value; /* Internal value for various state. */ -} bson_iter_t BSON_ALIGNED_END (128); +} bson_iter_t; /** @@ -385,12 +351,11 @@ typedef struct { * memory allocations under certain circumstances such as reading from an * incoming mongo packet. */ - -BSON_ALIGNED_BEGIN (BSON_ALIGN_OF_PTR) +BSON_ALIGNED_BEGIN(BSON_ALIGN_OF_PTR) typedef struct { uint32_t type; - /*< private >*/ -} bson_reader_t BSON_ALIGNED_END (BSON_ALIGN_OF_PTR); + /**< private >**/ +} bson_reader_t BSON_ALIGNED_END(BSON_ALIGN_OF_PTR); /** @@ -408,75 +373,64 @@ typedef struct { * You may pre-maturely stop the visitation of fields by returning true in your * visitor. Returning false will continue visitation to further fields. */ -BSON_ALIGNED_BEGIN (8) typedef struct { /* run before / after descending into a document */ - bool (*visit_before) (const bson_iter_t *iter, const char *key, void *data); - bool (*visit_after) (const bson_iter_t *iter, const char *key, void *data); + bool(BSON_CALL *visit_before)(const bson_iter_t *iter, const char *key, void *data); + bool(BSON_CALL *visit_after)(const bson_iter_t *iter, const char *key, void *data); /* corrupt BSON, or unsupported type and visit_unsupported_type not set */ - void (*visit_corrupt) (const bson_iter_t *iter, void *data); + void(BSON_CALL *visit_corrupt)(const bson_iter_t *iter, void *data); /* normal bson field callbacks */ - bool (*visit_double) (const bson_iter_t *iter, const char *key, double v_double, void *data); - bool (*visit_utf8) (const bson_iter_t *iter, const char *key, size_t v_utf8_len, const char *v_utf8, void *data); - bool (*visit_document) (const bson_iter_t *iter, const char *key, const bson_t *v_document, void *data); - bool (*visit_array) (const bson_iter_t *iter, const char *key, const bson_t *v_array, void *data); - bool (*visit_binary) (const bson_iter_t *iter, - const char *key, - bson_subtype_t v_subtype, - size_t v_binary_len, - const uint8_t *v_binary, - void *data); + bool(BSON_CALL *visit_double)(const bson_iter_t *iter, const char *key, double v_double, void *data); + bool(BSON_CALL *visit_utf8)( + const bson_iter_t *iter, const char *key, size_t v_utf8_len, const char *v_utf8, void *data); + bool(BSON_CALL *visit_document)(const bson_iter_t *iter, const char *key, const bson_t *v_document, void *data); + bool(BSON_CALL *visit_array)(const bson_iter_t *iter, const char *key, const bson_t *v_array, void *data); + bool(BSON_CALL *visit_binary)(const bson_iter_t *iter, + const char *key, + bson_subtype_t v_subtype, + size_t v_binary_len, + const uint8_t *v_binary, + void *data); /* normal field with deprecated "Undefined" BSON type */ - bool (*visit_undefined) (const bson_iter_t *iter, const char *key, void *data); - bool (*visit_oid) (const bson_iter_t *iter, const char *key, const bson_oid_t *v_oid, void *data); - bool (*visit_bool) (const bson_iter_t *iter, const char *key, bool v_bool, void *data); - bool (*visit_date_time) (const bson_iter_t *iter, const char *key, int64_t msec_since_epoch, void *data); - bool (*visit_null) (const bson_iter_t *iter, const char *key, void *data); - bool (*visit_regex) ( + bool(BSON_CALL *visit_undefined)(const bson_iter_t *iter, const char *key, void *data); + bool(BSON_CALL *visit_oid)(const bson_iter_t *iter, const char *key, const bson_oid_t *v_oid, void *data); + bool(BSON_CALL *visit_bool)(const bson_iter_t *iter, const char *key, bool v_bool, void *data); + bool(BSON_CALL *visit_date_time)(const bson_iter_t *iter, const char *key, int64_t msec_since_epoch, void *data); + bool(BSON_CALL *visit_null)(const bson_iter_t *iter, const char *key, void *data); + bool(BSON_CALL *visit_regex)( const bson_iter_t *iter, const char *key, const char *v_regex, const char *v_options, void *data); - bool (*visit_dbpointer) (const bson_iter_t *iter, - const char *key, - size_t v_collection_len, - const char *v_collection, - const bson_oid_t *v_oid, - void *data); - bool (*visit_code) (const bson_iter_t *iter, const char *key, size_t v_code_len, const char *v_code, void *data); - bool (*visit_symbol) ( + bool(BSON_CALL *visit_dbpointer)(const bson_iter_t *iter, + const char *key, + size_t v_collection_len, + const char *v_collection, + const bson_oid_t *v_oid, + void *data); + bool(BSON_CALL *visit_code)( + const bson_iter_t *iter, const char *key, size_t v_code_len, const char *v_code, void *data); + bool(BSON_CALL *visit_symbol)( const bson_iter_t *iter, const char *key, size_t v_symbol_len, const char *v_symbol, void *data); - bool (*visit_codewscope) (const bson_iter_t *iter, - const char *key, - size_t v_code_len, - const char *v_code, - const bson_t *v_scope, - void *data); - bool (*visit_int32) (const bson_iter_t *iter, const char *key, int32_t v_int32, void *data); - bool (*visit_timestamp) ( + bool(BSON_CALL *visit_codewscope)(const bson_iter_t *iter, + const char *key, + size_t v_code_len, + const char *v_code, + const bson_t *v_scope, + void *data); + bool(BSON_CALL *visit_int32)(const bson_iter_t *iter, const char *key, int32_t v_int32, void *data); + bool(BSON_CALL *visit_timestamp)( const bson_iter_t *iter, const char *key, uint32_t v_timestamp, uint32_t v_increment, void *data); - bool (*visit_int64) (const bson_iter_t *iter, const char *key, int64_t v_int64, void *data); - bool (*visit_maxkey) (const bson_iter_t *iter, const char *key, void *data); - bool (*visit_minkey) (const bson_iter_t *iter, const char *key, void *data); + bool(BSON_CALL *visit_int64)(const bson_iter_t *iter, const char *key, int64_t v_int64, void *data); + bool(BSON_CALL *visit_maxkey)(const bson_iter_t *iter, const char *key, void *data); + bool(BSON_CALL *visit_minkey)(const bson_iter_t *iter, const char *key, void *data); /* if set, called instead of visit_corrupt when an apparently valid BSON * includes an unrecognized field type (reading future version of BSON) */ - void (*visit_unsupported_type) (const bson_iter_t *iter, const char *key, uint32_t type_code, void *data); - bool (*visit_decimal128) (const bson_iter_t *iter, - const char *key, - const bson_decimal128_t *v_decimal128, - void *data); + void(BSON_CALL *visit_unsupported_type)(const bson_iter_t *iter, const char *key, uint32_t type_code, void *data); + bool(BSON_CALL *visit_decimal128)(const bson_iter_t *iter, + const char *key, + const bson_decimal128_t *v_decimal128, + void *data); void *padding[7]; -} bson_visitor_t BSON_ALIGNED_END (8); - -#define BSON_ERROR_BUFFER_SIZE 504 - -BSON_ALIGNED_BEGIN (8) -typedef struct _bson_error_t { - uint32_t domain; - uint32_t code; - char message[BSON_ERROR_BUFFER_SIZE]; -} bson_error_t BSON_ALIGNED_END (8); - - -BSON_STATIC_ASSERT2 (error_t, sizeof (bson_error_t) == 512); +} bson_visitor_t; /** @@ -491,7 +445,7 @@ BSON_STATIC_ASSERT2 (error_t, sizeof (bson_error_t) == 512); * Returns: The next power of 2 from @v. */ static BSON_INLINE size_t -bson_next_power_of_two (size_t v) +bson_next_power_of_two(size_t v) { v--; v |= v >> 1; @@ -509,7 +463,7 @@ bson_next_power_of_two (size_t v) static BSON_INLINE bool -bson_is_power_of_two (uint32_t v) +bson_is_power_of_two(uint32_t v) { return ((v != 0) && ((v & (v - 1)) == 0)); } diff --git a/bsonjs/bson/bson-utf8.c b/bsonjs/bson/bson-utf8.c index 61fa985..6ad08f4 100644 --- a/bsonjs/bson/bson-utf8.c +++ b/bsonjs/bson/bson-utf8.c @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,70 +15,18 @@ */ -#include - -#include -#include #include +#include +#include +#include +#include -/* - *-------------------------------------------------------------------------- - * - * _bson_utf8_get_sequence -- - * - * Determine the sequence length of the first UTF-8 character in - * @utf8. The sequence length is stored in @seq_length and the mask - * for the first character is stored in @first_mask. - * - * Returns: - * None. - * - * Side effects: - * @seq_length is set. - * @first_mask is set. - * - *-------------------------------------------------------------------------- - */ - -static BSON_INLINE void -_bson_utf8_get_sequence (const char *utf8, /* IN */ - uint8_t *seq_length, /* OUT */ - uint8_t *first_mask) /* OUT */ -{ - unsigned char c = *(const unsigned char *) utf8; - uint8_t m; - uint8_t n; - - /* - * See the following[1] for a description of what the given multi-byte - * sequences will be based on the bits set of the first byte. We also need - * to mask the first byte based on that. All subsequent bytes are masked - * against 0x3F. - * - * [1] http://www.joelonsoftware.com/articles/Unicode.html - */ +#include - if ((c & 0x80) == 0) { - n = 1; - m = 0x7F; - } else if ((c & 0xE0) == 0xC0) { - n = 2; - m = 0x1F; - } else if ((c & 0xF0) == 0xE0) { - n = 3; - m = 0x0F; - } else if ((c & 0xF8) == 0xF0) { - n = 4; - m = 0x07; - } else { - n = 0; - m = 0; - } +#include - *seq_length = n; - *first_mask = m; -} +#include /* @@ -96,10 +44,14 @@ _bson_utf8_get_sequence (const char *utf8, /* IN */ * However, some languages such as Python can send UTF-8 encoded * strings with NUL's in them. * + * Note that the two-byte sequence "C0 80" is also interpreted as an + * internal NUL, for historical reasons. This sequence is considered + * invalid according to RFC3629. + * * Parameters: * @utf8: A UTF-8 encoded string. * @utf8_len: The length of @utf8 in bytes. - * @allow_null: If \0 is allowed within @utf8, excluding trailing \0. + * @allow_null: If the single "00" byte or two-byte sequence "C0 80" are allowed internally within @utf8. * * Returns: * true if @utf8 is valid UTF-8. otherwise false. @@ -111,9 +63,9 @@ _bson_utf8_get_sequence (const char *utf8, /* IN */ */ bool -bson_utf8_validate (const char *utf8, /* IN */ - size_t utf8_len, /* IN */ - bool allow_null) /* IN */ +bson_utf8_validate(const char *utf8, /* IN */ + size_t utf8_len, /* IN */ + bool allow_null) /* IN */ { bson_unichar_t c; uint8_t first_mask; @@ -121,10 +73,10 @@ bson_utf8_validate (const char *utf8, /* IN */ size_t i; size_t j; - BSON_ASSERT (utf8); + BSON_ASSERT(utf8); for (i = 0; i < utf8_len; i += seq_length) { - _bson_utf8_get_sequence (&utf8[i], &seq_length, &first_mask); + mcommon_utf8_get_sequence(&utf8[i], &seq_length, &first_mask); /* * Ensure we have a valid multi-byte sequence length. @@ -244,6 +196,10 @@ bson_utf8_validate (const char *utf8, /* IN */ * byte is found before @utf8_len bytes, it will be converted to the * two byte UTF-8 sequence. * + * The two-byte sequence "C0 80" is also interpreted as an internal NUL, + * for historical reasons. This sequence is considered invalid according + * to RFC3629. + * * Parameters: * @utf8: A UTF-8 encoded string. * @utf8_len: The length of @utf8 in bytes or -1 if NUL terminated. @@ -258,73 +214,46 @@ bson_utf8_validate (const char *utf8, /* IN */ */ char * -bson_utf8_escape_for_json (const char *utf8, /* IN */ - ssize_t utf8_len) /* IN */ +bson_utf8_escape_for_json(const char *utf8, /* IN */ + ssize_t utf8_len) /* IN */ { - bson_unichar_t c; - bson_string_t *str; - bool length_provided = true; - const char *end; - - BSON_ASSERT (utf8); - - str = bson_string_new (NULL); + uint32_t len32; + bool allow_nul; if (utf8_len < 0) { - length_provided = false; - utf8_len = strlen (utf8); - } - - end = utf8 + utf8_len; - - while (utf8 < end) { - c = bson_utf8_get_char (utf8); - - switch (c) { - case '\\': - case '"': - bson_string_append_c (str, '\\'); - bson_string_append_unichar (str, c); - break; - case '\b': - bson_string_append (str, "\\b"); - break; - case '\f': - bson_string_append (str, "\\f"); - break; - case '\n': - bson_string_append (str, "\\n"); - break; - case '\r': - bson_string_append (str, "\\r"); - break; - case '\t': - bson_string_append (str, "\\t"); - break; - default: - if (c < ' ') { - bson_string_append_printf (str, "\\u%04x", (unsigned) c); - } else { - bson_string_append_unichar (str, c); - } - break; + size_t sizet_len = strlen(utf8); + if (sizet_len < UINT32_MAX) { + len32 = (uint32_t)sizet_len; + allow_nul = false; + } else { + return NULL; } - - if (c) { - utf8 = bson_utf8_next_char (utf8); + } else { + if (mlib_in_range(uint32_t, utf8_len) && (uint32_t)utf8_len < UINT32_MAX) { + len32 = utf8_len; + allow_nul = true; } else { - if (length_provided && !*utf8) { - /* we escaped nil as '\u0000', now advance past it */ - utf8++; - } else { - /* invalid UTF-8 */ - bson_string_free (str, true); - return NULL; - } + return NULL; } } - return bson_string_free (str, false); + /* The new private implementation of mcommon_json_append_escaped() avoids + * parsing UTF-8 sequences at all in most cases. It preserves the validity + * of valid sequences, but it will not catch most UTF-8 errors. For compatibility + * at the expense of performance, we emulate the old behavior in this wrapper. + */ + if (!bson_utf8_validate(utf8, (size_t)len32, allow_nul)) { + return NULL; + } + + mcommon_string_append_t append; + mcommon_string_new_with_capacity_as_append(&append, len32); + if (mcommon_json_append_escaped(&append, utf8, len32, allow_nul)) { + return mcommon_string_from_append_destroy_with_steal(&append); + } else { + mcommon_string_from_append_destroy(&append); + return NULL; + } } @@ -348,16 +277,16 @@ bson_utf8_escape_for_json (const char *utf8, /* IN */ */ bson_unichar_t -bson_utf8_get_char (const char *utf8) /* IN */ +bson_utf8_get_char(const char *utf8) /* IN */ { bson_unichar_t c; uint8_t mask; uint8_t num; int i; - BSON_ASSERT (utf8); + BSON_ASSERT(utf8); - _bson_utf8_get_sequence (utf8, &num, &mask); + mcommon_utf8_get_sequence(utf8, &num, &mask); c = (*utf8) & mask; for (i = 1; i < num; i++) { @@ -389,14 +318,14 @@ bson_utf8_get_char (const char *utf8) /* IN */ */ const char * -bson_utf8_next_char (const char *utf8) /* IN */ +bson_utf8_next_char(const char *utf8) /* IN */ { uint8_t mask; uint8_t num; - BSON_ASSERT (utf8); + BSON_ASSERT(utf8); - _bson_utf8_get_sequence (utf8, &num, &mask); + mcommon_utf8_get_sequence(utf8, &num, &mask); return utf8 + num; } @@ -426,32 +355,10 @@ bson_utf8_next_char (const char *utf8) /* IN */ */ void -bson_utf8_from_unichar (bson_unichar_t unichar, /* IN */ - char utf8[BSON_ENSURE_ARRAY_PARAM_SIZE (6)], /* OUT */ - uint32_t *len) /* OUT */ +bson_utf8_from_unichar(bson_unichar_t unichar, /* IN */ + char utf8[BSON_ENSURE_ARRAY_PARAM_SIZE(6)], /* OUT */ + uint32_t *len) /* OUT */ { - BSON_ASSERT (utf8); - BSON_ASSERT (len); - - if (unichar <= 0x7F) { - utf8[0] = unichar; - *len = 1; - } else if (unichar <= 0x7FF) { - *len = 2; - utf8[0] = 0xC0 | ((unichar >> 6) & 0x3F); - utf8[1] = 0x80 | ((unichar) & 0x3F); - } else if (unichar <= 0xFFFF) { - *len = 3; - utf8[0] = 0xE0 | ((unichar >> 12) & 0xF); - utf8[1] = 0x80 | ((unichar >> 6) & 0x3F); - utf8[2] = 0x80 | ((unichar) & 0x3F); - } else if (unichar <= 0x1FFFFF) { - *len = 4; - utf8[0] = 0xF0 | ((unichar >> 18) & 0x7); - utf8[1] = 0x80 | ((unichar >> 12) & 0x3F); - utf8[2] = 0x80 | ((unichar >> 6) & 0x3F); - utf8[3] = 0x80 | ((unichar) & 0x3F); - } else { - *len = 0; - } + // Inlined implementation from common-utf8-private + mcommon_utf8_from_unichar(unichar, utf8, len); } diff --git a/bsonjs/bson/bson-utf8.h b/bsonjs/bson/bson-utf8.h index af08596..7720879 100644 --- a/bsonjs/bson/bson-utf8.h +++ b/bsonjs/bson/bson-utf8.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,23 +21,23 @@ #define BSON_UTF8_H -#include #include +#include BSON_BEGIN_DECLS -BSON_EXPORT (bool) -bson_utf8_validate (const char *utf8, size_t utf8_len, bool allow_null); -BSON_EXPORT (char *) -bson_utf8_escape_for_json (const char *utf8, ssize_t utf8_len); -BSON_EXPORT (bson_unichar_t) -bson_utf8_get_char (const char *utf8); -BSON_EXPORT (const char *) -bson_utf8_next_char (const char *utf8); -BSON_EXPORT (void) -bson_utf8_from_unichar (bson_unichar_t unichar, char utf8[6], uint32_t *len); +BSON_EXPORT(bool) +bson_utf8_validate(const char *utf8, size_t utf8_len, bool allow_null); +BSON_EXPORT(char *) +bson_utf8_escape_for_json(const char *utf8, ssize_t utf8_len); +BSON_EXPORT(bson_unichar_t) +bson_utf8_get_char(const char *utf8); +BSON_EXPORT(const char *) +bson_utf8_next_char(const char *utf8); +BSON_EXPORT(void) +bson_utf8_from_unichar(bson_unichar_t unichar, char utf8[6], uint32_t *len); BSON_END_DECLS diff --git a/bsonjs/bson/bson-value.c b/bsonjs/bson/bson-value.c index 3e3c468..3a38e70 100644 --- a/bsonjs/bson/bson-value.c +++ b/bsonjs/bson/bson-value.c @@ -1,5 +1,5 @@ /* - * Copyright 2014 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,18 +15,22 @@ */ -#include -#include #include + +#include + #include +#include + +#include void -bson_value_copy (const bson_value_t *src, /* IN */ - bson_value_t *dst) /* OUT */ +bson_value_copy(const bson_value_t *src, /* IN */ + bson_value_t *dst) /* OUT */ { - BSON_ASSERT (src); - BSON_ASSERT (dst); + BSON_ASSERT(src); + BSON_ASSERT(dst); dst->value_type = src->value_type; @@ -35,27 +39,37 @@ bson_value_copy (const bson_value_t *src, /* IN */ dst->value.v_double = src->value.v_double; break; case BSON_TYPE_UTF8: - dst->value.v_utf8.len = src->value.v_utf8.len; - dst->value.v_utf8.str = bson_malloc (src->value.v_utf8.len + 1); - memcpy (dst->value.v_utf8.str, src->value.v_utf8.str, dst->value.v_utf8.len); - dst->value.v_utf8.str[dst->value.v_utf8.len] = '\0'; + BSON_ASSERT(mlib_in_range(size_t, src->value.v_utf8.len)); + size_t utf8_len_sz = (size_t)src->value.v_utf8.len; + if (utf8_len_sz == SIZE_MAX) { + // If the string is at maximum length, do not NULL terminate. The source necessarily cannot fit it. + dst->value.v_utf8.len = src->value.v_utf8.len; + dst->value.v_utf8.str = bson_malloc(utf8_len_sz); + memcpy(dst->value.v_utf8.str, src->value.v_utf8.str, dst->value.v_utf8.len); + } else { + // There is room in destination to NULL terminate. + dst->value.v_utf8.len = src->value.v_utf8.len; + dst->value.v_utf8.str = bson_malloc(utf8_len_sz + 1); + memcpy(dst->value.v_utf8.str, src->value.v_utf8.str, dst->value.v_utf8.len); + dst->value.v_utf8.str[dst->value.v_utf8.len] = '\0'; + } break; case BSON_TYPE_DOCUMENT: case BSON_TYPE_ARRAY: dst->value.v_doc.data_len = src->value.v_doc.data_len; - dst->value.v_doc.data = bson_malloc (src->value.v_doc.data_len); - memcpy (dst->value.v_doc.data, src->value.v_doc.data, dst->value.v_doc.data_len); + dst->value.v_doc.data = bson_malloc(src->value.v_doc.data_len); + memcpy(dst->value.v_doc.data, src->value.v_doc.data, dst->value.v_doc.data_len); break; case BSON_TYPE_BINARY: dst->value.v_binary.subtype = src->value.v_binary.subtype; dst->value.v_binary.data_len = src->value.v_binary.data_len; - dst->value.v_binary.data = bson_malloc (src->value.v_binary.data_len); + dst->value.v_binary.data = bson_malloc(src->value.v_binary.data_len); if (dst->value.v_binary.data_len) { - memcpy (dst->value.v_binary.data, src->value.v_binary.data, dst->value.v_binary.data_len); + memcpy(dst->value.v_binary.data, src->value.v_binary.data, dst->value.v_binary.data_len); } break; case BSON_TYPE_OID: - bson_oid_copy (&src->value.v_oid, &dst->value.v_oid); + bson_oid_copy(&src->value.v_oid, &dst->value.v_oid); break; case BSON_TYPE_BOOL: dst->value.v_bool = src->value.v_bool; @@ -64,38 +78,49 @@ bson_value_copy (const bson_value_t *src, /* IN */ dst->value.v_datetime = src->value.v_datetime; break; case BSON_TYPE_REGEX: - dst->value.v_regex.regex = bson_strdup (src->value.v_regex.regex); - dst->value.v_regex.options = bson_strdup (src->value.v_regex.options); + dst->value.v_regex.regex = bson_strdup(src->value.v_regex.regex); + dst->value.v_regex.options = bson_strdup(src->value.v_regex.options); break; case BSON_TYPE_DBPOINTER: + BSON_ASSERT(mlib_in_range(size_t, src->value.v_dbpointer.collection_len)); + size_t dbpointer_len_sz = (size_t)src->value.v_dbpointer.collection_len; + BSON_ASSERT(dbpointer_len_sz <= SIZE_MAX - 1); dst->value.v_dbpointer.collection_len = src->value.v_dbpointer.collection_len; - dst->value.v_dbpointer.collection = bson_malloc (src->value.v_dbpointer.collection_len + 1); - memcpy ( + dst->value.v_dbpointer.collection = bson_malloc(dbpointer_len_sz + 1); + memcpy( dst->value.v_dbpointer.collection, src->value.v_dbpointer.collection, dst->value.v_dbpointer.collection_len); dst->value.v_dbpointer.collection[dst->value.v_dbpointer.collection_len] = '\0'; - bson_oid_copy (&src->value.v_dbpointer.oid, &dst->value.v_dbpointer.oid); + bson_oid_copy(&src->value.v_dbpointer.oid, &dst->value.v_dbpointer.oid); break; case BSON_TYPE_CODE: + BSON_ASSERT(mlib_in_range(size_t, src->value.v_code.code_len)); + size_t code_len_sz = (size_t)src->value.v_code.code_len; + BSON_ASSERT(code_len_sz <= SIZE_MAX - 1); dst->value.v_code.code_len = src->value.v_code.code_len; - dst->value.v_code.code = bson_malloc (src->value.v_code.code_len + 1); - memcpy (dst->value.v_code.code, src->value.v_code.code, dst->value.v_code.code_len); + dst->value.v_code.code = bson_malloc(code_len_sz + 1); + memcpy(dst->value.v_code.code, src->value.v_code.code, dst->value.v_code.code_len); dst->value.v_code.code[dst->value.v_code.code_len] = '\0'; break; case BSON_TYPE_SYMBOL: + BSON_ASSERT(mlib_in_range(size_t, src->value.v_symbol.len)); + size_t symbol_len_sz = (size_t)src->value.v_symbol.len; + BSON_ASSERT(symbol_len_sz <= SIZE_MAX - 1); dst->value.v_symbol.len = src->value.v_symbol.len; - dst->value.v_symbol.symbol = bson_malloc (src->value.v_symbol.len + 1); - memcpy (dst->value.v_symbol.symbol, src->value.v_symbol.symbol, dst->value.v_symbol.len); + dst->value.v_symbol.symbol = bson_malloc(symbol_len_sz + 1); + memcpy(dst->value.v_symbol.symbol, src->value.v_symbol.symbol, dst->value.v_symbol.len); dst->value.v_symbol.symbol[dst->value.v_symbol.len] = '\0'; break; case BSON_TYPE_CODEWSCOPE: + BSON_ASSERT(mlib_in_range(size_t, src->value.v_codewscope.code_len)); + size_t codewscope_len_sz = (size_t)src->value.v_codewscope.code_len; + BSON_ASSERT(codewscope_len_sz <= SIZE_MAX - 1); dst->value.v_codewscope.code_len = src->value.v_codewscope.code_len; - dst->value.v_codewscope.code = bson_malloc (src->value.v_codewscope.code_len + 1); - memcpy (dst->value.v_codewscope.code, src->value.v_codewscope.code, dst->value.v_codewscope.code_len); + dst->value.v_codewscope.code = bson_malloc(codewscope_len_sz + 1); + memcpy(dst->value.v_codewscope.code, src->value.v_codewscope.code, dst->value.v_codewscope.code_len); dst->value.v_codewscope.code[dst->value.v_codewscope.code_len] = '\0'; dst->value.v_codewscope.scope_len = src->value.v_codewscope.scope_len; - dst->value.v_codewscope.scope_data = bson_malloc (src->value.v_codewscope.scope_len); - memcpy ( - dst->value.v_codewscope.scope_data, src->value.v_codewscope.scope_data, dst->value.v_codewscope.scope_len); + dst->value.v_codewscope.scope_data = bson_malloc(src->value.v_codewscope.scope_len); + memcpy(dst->value.v_codewscope.scope_data, src->value.v_codewscope.scope_data, dst->value.v_codewscope.scope_len); break; case BSON_TYPE_INT32: dst->value.v_int32 = src->value.v_int32; @@ -117,14 +142,14 @@ bson_value_copy (const bson_value_t *src, /* IN */ break; case BSON_TYPE_EOD: default: - BSON_ASSERT (false); + BSON_ASSERT(false); return; } } void -bson_value_destroy (bson_value_t *value) /* IN */ +bson_value_destroy(bson_value_t *value) /* IN */ { if (!value) { return; @@ -132,31 +157,31 @@ bson_value_destroy (bson_value_t *value) /* IN */ switch (value->value_type) { case BSON_TYPE_UTF8: - bson_free (value->value.v_utf8.str); + bson_free(value->value.v_utf8.str); break; case BSON_TYPE_DOCUMENT: case BSON_TYPE_ARRAY: - bson_free (value->value.v_doc.data); + bson_free(value->value.v_doc.data); break; case BSON_TYPE_BINARY: - bson_free (value->value.v_binary.data); + bson_free(value->value.v_binary.data); break; case BSON_TYPE_REGEX: - bson_free (value->value.v_regex.regex); - bson_free (value->value.v_regex.options); + bson_free(value->value.v_regex.regex); + bson_free(value->value.v_regex.options); break; case BSON_TYPE_DBPOINTER: - bson_free (value->value.v_dbpointer.collection); + bson_free(value->value.v_dbpointer.collection); break; case BSON_TYPE_CODE: - bson_free (value->value.v_code.code); + bson_free(value->value.v_code.code); break; case BSON_TYPE_SYMBOL: - bson_free (value->value.v_symbol.symbol); + bson_free(value->value.v_symbol.symbol); break; case BSON_TYPE_CODEWSCOPE: - bson_free (value->value.v_codewscope.code); - bson_free (value->value.v_codewscope.scope_data); + bson_free(value->value.v_codewscope.code); + bson_free(value->value.v_codewscope.scope_data); break; case BSON_TYPE_DOUBLE: case BSON_TYPE_UNDEFINED: diff --git a/bsonjs/bson/bson-value.h b/bsonjs/bson/bson-value.h index 4175690..cbd66d7 100644 --- a/bsonjs/bson/bson-value.h +++ b/bsonjs/bson/bson-value.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,17 +21,17 @@ #define BSON_VALUE_H -#include #include +#include BSON_BEGIN_DECLS -BSON_EXPORT (void) -bson_value_copy (const bson_value_t *src, bson_value_t *dst); -BSON_EXPORT (void) -bson_value_destroy (bson_value_t *value); +BSON_EXPORT(void) +bson_value_copy(const bson_value_t *src, bson_value_t *dst); +BSON_EXPORT(void) +bson_value_destroy(bson_value_t *value); BSON_END_DECLS diff --git a/bsonjs/bson/bson-vector-private.h b/bsonjs/bson/bson-vector-private.h new file mode 100644 index 0000000..100da3e --- /dev/null +++ b/bsonjs/bson/bson-vector-private.h @@ -0,0 +1,64 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef BSON_VECTOR_PRIVATE_H +#define BSON_VECTOR_PRIVATE_H + +#include // IWYU pragma: export + +// + +#include +#include + +BSON_BEGIN_DECLS + + +typedef enum { + BSON_VECTOR_ELEMENT_SIGNED_INT = 0, + BSON_VECTOR_ELEMENT_UNSIGNED_INT = 1, + BSON_VECTOR_ELEMENT_FLOAT = 2, +} bson_vector_element_type_t; + +typedef enum { + BSON_VECTOR_ELEMENT_1_BIT = 0, + BSON_VECTOR_ELEMENT_8_BITS = 3, + BSON_VECTOR_ELEMENT_32_BITS = 7, +} bson_vector_element_size_t; + + +static BSON_INLINE uint8_t +bson_vector_header_byte_0(bson_vector_element_type_t element_type, bson_vector_element_size_t element_size) +{ + BSON_ASSERT((unsigned)element_type <= 0x0f); + BSON_ASSERT((unsigned)element_size <= 0x0f); + return (uint8_t)(((unsigned)element_type << 4) | (unsigned)element_size); +} + +// See also `bson_vector_padding_from_header_byte_1` defined in for use by public inline functions. +static BSON_INLINE uint8_t +bson_vector_header_byte_1(size_t padding) +{ + BSON_ASSERT(padding <= 7); + return (uint8_t)padding; +} + + +BSON_END_DECLS + +#endif /* BSON_VECTOR_PRIVATE_H */ diff --git a/bsonjs/bson/bson-vector.c b/bsonjs/bson/bson-vector.c new file mode 100644 index 0000000..b934b30 --- /dev/null +++ b/bsonjs/bson/bson-vector.c @@ -0,0 +1,686 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +#include + +#include + + +static BSON_INLINE bool +bson_vector_binary_header_impl_init(bson_vector_binary_header_impl_t *header_out, + const uint8_t *binary_data, + uint32_t binary_data_len) +{ + if (binary_data_len >= BSON_VECTOR_HEADER_LEN) { + memcpy(header_out->bytes, binary_data, BSON_VECTOR_HEADER_LEN); + return true; + } else { + return false; + } +} + +static BSON_INLINE bool +bson_vector_int8_validate(bson_vector_binary_header_impl_t header) +{ + return header.bytes[0] == bson_vector_header_byte_0(BSON_VECTOR_ELEMENT_SIGNED_INT, BSON_VECTOR_ELEMENT_8_BITS) && + header.bytes[1] == bson_vector_header_byte_1(0); +} + +static BSON_INLINE bool +bson_vector_float32_validate(bson_vector_binary_header_impl_t header, uint32_t binary_data_len) +{ + return (binary_data_len - BSON_VECTOR_HEADER_LEN) % sizeof(float) == 0 && + header.bytes[0] == bson_vector_header_byte_0(BSON_VECTOR_ELEMENT_FLOAT, BSON_VECTOR_ELEMENT_32_BITS) && + header.bytes[1] == bson_vector_header_byte_1(0); +} + +static BSON_INLINE bool +bson_vector_packed_bit_validate(bson_vector_binary_header_impl_t header, + const uint8_t *binary_data, + uint32_t binary_data_len) +{ + if (header.bytes[0] == bson_vector_header_byte_0(BSON_VECTOR_ELEMENT_UNSIGNED_INT, BSON_VECTOR_ELEMENT_1_BIT)) { + size_t padding = bson_vector_padding_from_header_byte_1(header.bytes[1]); + if (header.bytes[1] != bson_vector_header_byte_1(padding)) { + return false; + } + uint32_t vector_data_len = binary_data_len - BSON_VECTOR_HEADER_LEN; + if (vector_data_len == 0) { + return padding == 0; + } else { + // We need to read the last byte of the binary block to validate that unused bits are zero. + uint8_t last_data_byte = binary_data[binary_data_len - 1]; + uint8_t mask_of_unused_bits = (uint8_t)((1u << padding) - 1u); + return (last_data_byte & mask_of_unused_bits) == 0; + } + } else { + return false; + } +} + + +bool +bson_vector_int8_view_init(bson_vector_int8_view_t *view_out, uint8_t *binary_data, uint32_t binary_data_len) +{ + BSON_OPTIONAL_PARAM(view_out); + BSON_ASSERT_PARAM(binary_data); + bson_vector_binary_header_impl_t header; + if (bson_vector_binary_header_impl_init(&header, binary_data, binary_data_len) && + bson_vector_int8_validate(header)) { + if (view_out) { + *view_out = (bson_vector_int8_view_t){ + .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; + } + return true; + } else { + return false; + } +} + +bool +bson_vector_int8_const_view_init(bson_vector_int8_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len) +{ + BSON_OPTIONAL_PARAM(view_out); + BSON_ASSERT_PARAM(binary_data); + bson_vector_binary_header_impl_t header; + if (bson_vector_binary_header_impl_init(&header, binary_data, binary_data_len) && + bson_vector_int8_validate(header)) { + if (view_out) { + *view_out = (bson_vector_int8_const_view_t){ + .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; + } + return true; + } else { + return false; + } +} + +bool +bson_vector_float32_view_init(bson_vector_float32_view_t *view_out, uint8_t *binary_data, uint32_t binary_data_len) +{ + BSON_OPTIONAL_PARAM(view_out); + BSON_ASSERT_PARAM(binary_data); + bson_vector_binary_header_impl_t header; + if (bson_vector_binary_header_impl_init(&header, binary_data, binary_data_len) && + bson_vector_float32_validate(header, binary_data_len)) { + if (view_out) { + *view_out = (bson_vector_float32_view_t){ + .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; + } + return true; + } else { + return false; + } +} + +bool +bson_vector_float32_const_view_init(bson_vector_float32_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len) +{ + BSON_OPTIONAL_PARAM(view_out); + BSON_ASSERT_PARAM(binary_data); + bson_vector_binary_header_impl_t header; + if (bson_vector_binary_header_impl_init(&header, binary_data, binary_data_len) && + bson_vector_float32_validate(header, binary_data_len)) { + if (view_out) { + *view_out = (bson_vector_float32_const_view_t){ + .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; + } + return true; + } else { + return false; + } +} + +bool +bson_vector_packed_bit_view_init(bson_vector_packed_bit_view_t *view_out, + uint8_t *binary_data, + uint32_t binary_data_len) +{ + BSON_OPTIONAL_PARAM(view_out); + BSON_ASSERT_PARAM(binary_data); + bson_vector_binary_header_impl_t header; + if (bson_vector_binary_header_impl_init(&header, binary_data, binary_data_len) && + bson_vector_packed_bit_validate(header, binary_data, binary_data_len)) { + if (view_out) { + *view_out = (bson_vector_packed_bit_view_t){ + .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; + } + return true; + } else { + return false; + } +} + +bool +bson_vector_packed_bit_const_view_init(bson_vector_packed_bit_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len) +{ + BSON_OPTIONAL_PARAM(view_out); + BSON_ASSERT_PARAM(binary_data); + bson_vector_binary_header_impl_t header; + if (bson_vector_binary_header_impl_init(&header, binary_data, binary_data_len) && + bson_vector_packed_bit_validate(header, binary_data, binary_data_len)) { + if (view_out) { + *view_out = (bson_vector_packed_bit_const_view_t){ + .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; + } + return true; + } else { + return false; + } +} + + +bool +bson_vector_int8_view_from_iter(bson_vector_int8_view_t *view_out, bson_iter_t *iter) +{ + BSON_OPTIONAL_PARAM(view_out); + BSON_ASSERT_PARAM(iter); + if (BSON_ITER_HOLDS_BINARY(iter)) { + uint32_t binary_len; + uint8_t *binary; + bson_iter_overwrite_binary(iter, BSON_SUBTYPE_VECTOR, &binary_len, &binary); + return binary && bson_vector_int8_view_init(view_out, binary, binary_len); + } else { + return false; + } +} + +bool +bson_vector_int8_const_view_from_iter(bson_vector_int8_const_view_t *view_out, const bson_iter_t *iter) +{ + BSON_OPTIONAL_PARAM(view_out); + BSON_ASSERT_PARAM(iter); + if (BSON_ITER_HOLDS_BINARY(iter)) { + bson_subtype_t subtype; + uint32_t binary_len; + const uint8_t *binary; + bson_iter_binary(iter, &subtype, &binary_len, &binary); + return binary && subtype == BSON_SUBTYPE_VECTOR && bson_vector_int8_const_view_init(view_out, binary, binary_len); + } else { + return false; + } +} + +bool +bson_vector_float32_view_from_iter(bson_vector_float32_view_t *view_out, bson_iter_t *iter) +{ + BSON_OPTIONAL_PARAM(view_out); + BSON_ASSERT_PARAM(iter); + if (BSON_ITER_HOLDS_BINARY(iter)) { + uint32_t binary_len; + uint8_t *binary; + bson_iter_overwrite_binary(iter, BSON_SUBTYPE_VECTOR, &binary_len, &binary); + return binary && bson_vector_float32_view_init(view_out, binary, binary_len); + } else { + return false; + } +} + +bool +bson_vector_float32_const_view_from_iter(bson_vector_float32_const_view_t *view_out, const bson_iter_t *iter) +{ + BSON_OPTIONAL_PARAM(view_out); + BSON_ASSERT_PARAM(iter); + if (BSON_ITER_HOLDS_BINARY(iter)) { + bson_subtype_t subtype; + uint32_t binary_len; + const uint8_t *binary; + bson_iter_binary(iter, &subtype, &binary_len, &binary); + return binary && subtype == BSON_SUBTYPE_VECTOR && + bson_vector_float32_const_view_init(view_out, binary, binary_len); + } else { + return false; + } +} + +bool +bson_vector_packed_bit_view_from_iter(bson_vector_packed_bit_view_t *view_out, bson_iter_t *iter) +{ + BSON_OPTIONAL_PARAM(view_out); + BSON_ASSERT_PARAM(iter); + if (BSON_ITER_HOLDS_BINARY(iter)) { + uint32_t binary_len; + uint8_t *binary; + bson_iter_overwrite_binary(iter, BSON_SUBTYPE_VECTOR, &binary_len, &binary); + return binary && bson_vector_packed_bit_view_init(view_out, binary, binary_len); + } else { + return false; + } +} + +bool +bson_vector_packed_bit_const_view_from_iter(bson_vector_packed_bit_const_view_t *view_out, const bson_iter_t *iter) +{ + BSON_OPTIONAL_PARAM(view_out); + BSON_ASSERT_PARAM(iter); + if (BSON_ITER_HOLDS_BINARY(iter)) { + bson_subtype_t subtype; + uint32_t binary_len; + const uint8_t *binary; + bson_iter_binary(iter, &subtype, &binary_len, &binary); + return binary && subtype == BSON_SUBTYPE_VECTOR && + bson_vector_packed_bit_const_view_init(view_out, binary, binary_len); + } else { + return false; + } +} + + +bool +bson_append_vector_int8_uninit( + bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_int8_view_t *view_out) +{ + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(view_out); + + uint32_t length = bson_vector_int8_binary_data_length(element_count); + if (length < BSON_VECTOR_HEADER_LEN) { + return false; + } + uint8_t *binary; + if (bson_append_binary_uninit(bson, key, key_length, BSON_SUBTYPE_VECTOR, &binary, length)) { + bson_vector_binary_header_impl_t header = { + .bytes[0] = bson_vector_header_byte_0(BSON_VECTOR_ELEMENT_SIGNED_INT, BSON_VECTOR_ELEMENT_8_BITS), + .bytes[1] = bson_vector_header_byte_1(0)}; + memcpy(binary, header.bytes, BSON_VECTOR_HEADER_LEN); + *view_out = + (bson_vector_int8_view_t){.binary.data = binary, .binary.data_len = length, .binary.header_copy = header}; + return true; + } else { + return false; + } +} + +bool +bson_append_vector_float32_uninit( + bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_float32_view_t *view_out) +{ + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(view_out); + + uint32_t length = bson_vector_float32_binary_data_length(element_count); + if (length < BSON_VECTOR_HEADER_LEN) { + return false; + } + uint8_t *binary; + if (bson_append_binary_uninit(bson, key, key_length, BSON_SUBTYPE_VECTOR, &binary, length)) { + bson_vector_binary_header_impl_t header = { + .bytes[0] = bson_vector_header_byte_0(BSON_VECTOR_ELEMENT_FLOAT, BSON_VECTOR_ELEMENT_32_BITS), + .bytes[1] = bson_vector_header_byte_1(0)}; + memcpy(binary, header.bytes, BSON_VECTOR_HEADER_LEN); + *view_out = + (bson_vector_float32_view_t){.binary.data = binary, .binary.data_len = length, .binary.header_copy = header}; + return true; + } else { + return false; + } +} + +bool +bson_append_vector_packed_bit_uninit( + bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_packed_bit_view_t *view_out) +{ + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(view_out); + + uint32_t length = bson_vector_packed_bit_binary_data_length(element_count); + if (length < BSON_VECTOR_HEADER_LEN) { + return false; + } + uint8_t *binary; + if (bson_append_binary_uninit(bson, key, key_length, BSON_SUBTYPE_VECTOR, &binary, length)) { + mlib_diagnostic_push(); + mlib_msvc_warning(disable : 4146); + size_t padding = (size_t)7 & -element_count; + mlib_diagnostic_pop(); + + bson_vector_binary_header_impl_t header = { + .bytes[0] = bson_vector_header_byte_0(BSON_VECTOR_ELEMENT_UNSIGNED_INT, BSON_VECTOR_ELEMENT_1_BIT), + .bytes[1] = bson_vector_header_byte_1(padding)}; + memcpy(binary, header.bytes, BSON_VECTOR_HEADER_LEN); + if (element_count > 0 && padding > 0) { + // We must explicitly zero bits in the final byte that aren't part of any element. + // No reason to read-modify-write here, it's better to write the whole byte. + binary[length - 1u] = 0u; + } + *view_out = (bson_vector_packed_bit_view_t){ + .binary.data = binary, .binary.data_len = length, .binary.header_copy = header}; + return true; + } else { + return false; + } +} + +static bool +bson_vector_from_array_expect_key(const bson_iter_t *iter, uint32_t numeric_key, bson_error_t *error) +{ + char buffer[16]; + const char *key; + bson_uint32_to_string(numeric_key, &key, buffer, sizeof buffer); + if (0 == strcmp(key, bson_iter_key(iter))) { + return true; + } else { + bson_set_error(error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_KEY, + "expected BSON array key '%s', found key '%s'", + key, + bson_iter_key(iter)); + return false; + } +} + +static void +bson_vector_set_error_max_size(bson_error_t *error) +{ + bson_set_error(error, BSON_ERROR_VECTOR, BSON_VECTOR_ERROR_MAX_SIZE, "maximum BSON document size would be exceeded"); +} + +bool +bson_append_vector_int8_from_array( + bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error) +{ + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + BSON_ASSERT_PARAM(iter); + + uint32_t element_count = 0; + { + bson_iter_t validation_iter = *iter; + while (bson_iter_next(&validation_iter)) { + if (!bson_vector_from_array_expect_key(&validation_iter, element_count, error)) { + return false; + } + if (!BSON_ITER_HOLDS_INT(&validation_iter)) { + bson_set_error(error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE, + "expected int32 or int64 in BSON array key '%s', found item type 0x%02X", + bson_iter_key(&validation_iter), + (unsigned)bson_iter_type(&validation_iter)); + return false; + } + int64_t element_as_int64 = bson_iter_as_int64(&validation_iter); + if (element_as_int64 < INT8_MIN || element_as_int64 > INT8_MAX) { + bson_set_error(error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_VALUE, + "BSON array key '%s' value %" PRId64 " is out of range for vector of int8", + bson_iter_key(&validation_iter), + element_as_int64); + return false; + } + element_count++; + } + } + + bson_vector_int8_view_t view; + if (!bson_append_vector_int8_uninit(bson, key, key_length, element_count, &view)) { + bson_vector_set_error_max_size(error); + return false; + } + bson_iter_t copy_iter = *iter; + for (uint32_t i = 0; i < element_count; i++) { + BSON_ASSERT(bson_iter_next(©_iter)); + int8_t element = (int8_t)bson_iter_as_int64(©_iter); + BSON_ASSERT(bson_vector_int8_view_write(view, &element, 1, i)); + } + return true; +} + +bool +bson_append_vector_float32_from_array( + bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error) +{ + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + BSON_ASSERT_PARAM(iter); + + uint32_t element_count = 0; + { + bson_iter_t validation_iter = *iter; + while (bson_iter_next(&validation_iter)) { + if (!bson_vector_from_array_expect_key(&validation_iter, element_count, error)) { + return false; + } + if (!BSON_ITER_HOLDS_DOUBLE(&validation_iter)) { + bson_set_error(error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE, + "expected 'double' number type in BSON array key '%s', found item type 0x%02X", + bson_iter_key(&validation_iter), + (unsigned)bson_iter_type(&validation_iter)); + return false; + } + element_count++; + } + } + + bson_vector_float32_view_t view; + if (!bson_append_vector_float32_uninit(bson, key, key_length, element_count, &view)) { + bson_vector_set_error_max_size(error); + return false; + } + bson_iter_t copy_iter = *iter; + for (uint32_t i = 0; i < element_count; i++) { + BSON_ASSERT(bson_iter_next(©_iter)); + float element = (float)bson_iter_double(©_iter); + BSON_ASSERT(bson_vector_float32_view_write(view, &element, 1, i)); + } + return true; +} + +bool +bson_append_vector_packed_bit_from_array( + bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error) +{ + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + BSON_ASSERT_PARAM(iter); + + uint32_t element_count = 0; + { + bson_iter_t validation_iter = *iter; + while (bson_iter_next(&validation_iter)) { + if (!bson_vector_from_array_expect_key(&validation_iter, element_count, error)) { + return false; + } + if (!BSON_ITER_HOLDS_INT(&validation_iter) && !BSON_ITER_HOLDS_BOOL(&validation_iter)) { + bson_set_error(error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE, + "expected int32, int64, or bool in BSON array key '%s', found item type 0x%02X", + bson_iter_key(&validation_iter), + (unsigned)bson_iter_type(&validation_iter)); + return false; + } + int64_t element_as_int64 = bson_iter_as_int64(&validation_iter); + if (element_as_int64 < 0 || element_as_int64 > 1) { + bson_set_error(error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_VALUE, + "BSON array key '%s' value %" PRId64 " is out of range for vector of packed_bit", + bson_iter_key(&validation_iter), + element_as_int64); + return false; + } + element_count++; + } + } + + bson_vector_packed_bit_view_t view; + if (!bson_append_vector_packed_bit_uninit(bson, key, key_length, element_count, &view)) { + bson_vector_set_error_max_size(error); + return false; + } + bson_iter_t copy_iter = *iter; + for (uint32_t i = 0; i < element_count; i++) { + BSON_ASSERT(bson_iter_next(©_iter)); + bool element_as_bool = (bool)bson_iter_as_int64(©_iter); + BSON_ASSERT(bson_vector_packed_bit_view_pack_bool(view, &element_as_bool, 1, i)); + } + return true; +} + + +bool +bson_array_builder_append_vector_int8_elements(bson_array_builder_t *builder, bson_vector_int8_const_view_t view) +{ + BSON_ASSERT_PARAM(builder); + size_t length = bson_vector_int8_const_view_length(view); + for (size_t i = 0; i < length; i++) { + // Note, the zero initializer is only needed due to a false positive -Wmaybe-uninitialized warning in uncommon + // configurations where the compiler does not have visibility into memcpy(). + int8_t element = 0; + BSON_ASSERT(bson_vector_int8_const_view_read(view, &element, 1, i)); + if (!bson_array_builder_append_int32(builder, (int32_t)element)) { + return false; + } + } + return true; +} + +bool +bson_array_builder_append_vector_float32_elements(bson_array_builder_t *builder, bson_vector_float32_const_view_t view) +{ + BSON_ASSERT_PARAM(builder); + size_t length = bson_vector_float32_const_view_length(view); + for (size_t i = 0; i < length; i++) { + float element; + BSON_ASSERT(bson_vector_float32_const_view_read(view, &element, 1, i)); + if (!bson_array_builder_append_double(builder, (double)element)) { + return false; + } + } + return true; +} + +bool +bson_array_builder_append_vector_packed_bit_elements(bson_array_builder_t *builder, + bson_vector_packed_bit_const_view_t view) +{ + BSON_ASSERT_PARAM(builder); + size_t length = bson_vector_packed_bit_const_view_length(view); + for (size_t i = 0; i < length; i++) { + bool element; + BSON_ASSERT(bson_vector_packed_bit_const_view_unpack_bool(view, &element, 1, i)); + if (!bson_array_builder_append_int32(builder, element ? 1 : 0)) { + return false; + } + } + return true; +} + + +bool +bson_array_builder_append_vector_elements(bson_array_builder_t *builder, const bson_iter_t *iter) +{ + BSON_ASSERT_PARAM(builder); + BSON_ASSERT_PARAM(iter); + { + bson_vector_int8_const_view_t view; + if (bson_vector_int8_const_view_from_iter(&view, iter)) { + return bson_array_builder_append_vector_int8_elements(builder, view); + } + } + { + bson_vector_float32_const_view_t view; + if (bson_vector_float32_const_view_from_iter(&view, iter)) { + return bson_array_builder_append_vector_float32_elements(builder, view); + } + } + { + bson_vector_packed_bit_const_view_t view; + if (bson_vector_packed_bit_const_view_from_iter(&view, iter)) { + return bson_array_builder_append_vector_packed_bit_elements(builder, view); + } + } + return false; +} + + +bool +bson_append_array_from_vector_int8(bson_t *bson, const char *key, int key_length, bson_vector_int8_const_view_t view) +{ + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + bson_array_builder_t *child; + if (bson_append_array_builder_begin(bson, key, key_length, &child)) { + bool ok = bson_array_builder_append_vector_int8_elements(child, view); + return bson_append_array_builder_end(bson, child) && ok; + } else { + return false; + } +} + +bool +bson_append_array_from_vector_float32(bson_t *bson, + const char *key, + int key_length, + bson_vector_float32_const_view_t view) +{ + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + bson_array_builder_t *child; + if (bson_append_array_builder_begin(bson, key, key_length, &child)) { + bool ok = bson_array_builder_append_vector_float32_elements(child, view); + return bson_append_array_builder_end(bson, child) && ok; + } else { + return false; + } +} + +bool +bson_append_array_from_vector_packed_bit(bson_t *bson, + const char *key, + int key_length, + bson_vector_packed_bit_const_view_t view) +{ + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + bson_array_builder_t *child; + if (bson_append_array_builder_begin(bson, key, key_length, &child)) { + bool ok = bson_array_builder_append_vector_packed_bit_elements(child, view); + return bson_append_array_builder_end(bson, child) && ok; + } else { + return false; + } +} + + +bool +bson_append_array_from_vector(bson_t *bson, const char *key, int key_length, const bson_iter_t *iter) +{ + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + BSON_ASSERT_PARAM(iter); + bson_array_builder_t *child; + if (bson_append_array_builder_begin(bson, key, key_length, &child)) { + bool ok = bson_array_builder_append_vector_elements(child, iter); + return bson_append_array_builder_end(bson, child) && ok; + } else { + return false; + } +} diff --git a/bsonjs/bson/bson-vector.h b/bsonjs/bson/bson-vector.h new file mode 100644 index 0000000..f58c982 --- /dev/null +++ b/bsonjs/bson/bson-vector.h @@ -0,0 +1,615 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef BSON_VECTOR_H +#define BSON_VECTOR_H + +#include +#include +#include +#include +#include + +BSON_BEGIN_DECLS + + +// Length of the required header for BSON_SUBTYPE_VECTOR, in bytes +#define BSON_VECTOR_HEADER_LEN 2 + +// Forward declaration (typedef bson_array_builder_t in bson.h) +struct _bson_array_builder_t; + +/** @brief Error codes for domain BSON_ERROR_VECTOR */ +typedef enum { + BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE = 1, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_VALUE, + BSON_VECTOR_ERROR_ARRAY_KEY, + BSON_VECTOR_ERROR_MAX_SIZE, +} bson_vector_error_code_t; + + +/** @brief Implementation detail. A copy of the BSON_SUBTYPE_VECTOR header, suitable for pass-by-value. */ +typedef struct bson_vector_binary_header_impl_t { + uint8_t bytes[BSON_VECTOR_HEADER_LEN]; +} bson_vector_binary_header_impl_t; + +/** @brief Implementation detail. A reference to non-owned const BSON Binary data of subtype BSON_SUBTYPE_VECTOR */ +typedef struct bson_vector_binary_const_view_impl_t { + const uint8_t *data; + uint32_t data_len; + bson_vector_binary_header_impl_t header_copy; +} bson_vector_binary_const_view_impl_t; + +/** @brief Implementation detail. A reference to non-owned BSON Binary data of subtype BSON_SUBTYPE_VECTOR */ +typedef struct bson_vector_binary_view_impl_t { + uint8_t *data; + uint32_t data_len; + bson_vector_binary_header_impl_t header_copy; +} bson_vector_binary_view_impl_t; + +/** @brief Implementation detail. Obtain a const reference from a non-const reference without re-validating. */ +static BSON_INLINE bson_vector_binary_const_view_impl_t +bson_vector_binary_view_impl_as_const(bson_vector_binary_view_impl_t view) +{ + bson_vector_binary_const_view_impl_t result; + result.data = view.data; + result.data_len = view.data_len; + result.header_copy = view.header_copy; + return result; +} + + +/** @brief A reference to non-owned BSON Binary data holding a valid Vector of int8 element type */ +typedef struct bson_vector_int8_view_t { + bson_vector_binary_view_impl_t binary; +} bson_vector_int8_view_t; + +/** @brief A reference to non-owned const BSON Binary data holding a valid Vector of int8 element type */ +typedef struct bson_vector_int8_const_view_t { + bson_vector_binary_const_view_impl_t binary; +} bson_vector_int8_const_view_t; + +/** @brief A reference to non-owned BSON Binary data holding a valid Vector of float32 element type */ +typedef struct bson_vector_float32_view_t { + bson_vector_binary_view_impl_t binary; +} bson_vector_float32_view_t; + +/** @brief A reference to non-owned const BSON Binary data holding a valid Vector of float32 element type */ +typedef struct bson_vector_float32_const_view_t { + bson_vector_binary_const_view_impl_t binary; +} bson_vector_float32_const_view_t; + +/** @brief A reference to non-owned BSON Binary data holding a valid Vector of packed_bit */ +typedef struct bson_vector_packed_bit_view_t { + bson_vector_binary_view_impl_t binary; +} bson_vector_packed_bit_view_t; + +/** @brief A reference to non-owned const BSON Binary data holding a valid Vector of packed_bit */ +typedef struct bson_vector_packed_bit_const_view_t { + bson_vector_binary_const_view_impl_t binary; +} bson_vector_packed_bit_const_view_t; + + +static BSON_INLINE bson_vector_int8_const_view_t +bson_vector_int8_view_as_const(bson_vector_int8_view_t view) +{ + bson_vector_int8_const_view_t result; + result.binary = bson_vector_binary_view_impl_as_const(view.binary); + return result; +} + +static BSON_INLINE bson_vector_float32_const_view_t +bson_vector_float32_view_as_const(bson_vector_float32_view_t view) +{ + bson_vector_float32_const_view_t result; + result.binary = bson_vector_binary_view_impl_as_const(view.binary); + return result; +} + +static BSON_INLINE bson_vector_packed_bit_const_view_t +bson_vector_packed_bit_view_as_const(bson_vector_packed_bit_view_t view) +{ + bson_vector_packed_bit_const_view_t result; + result.binary = bson_vector_binary_view_impl_as_const(view.binary); + return result; +} + + +BSON_EXPORT(bool) +bson_vector_int8_view_init(bson_vector_int8_view_t *view_out, uint8_t *binary_data, uint32_t binary_data_len); + +BSON_EXPORT(bool) +bson_vector_int8_const_view_init(bson_vector_int8_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len); + +BSON_EXPORT(bool) +bson_vector_float32_view_init(bson_vector_float32_view_t *view_out, uint8_t *binary_data, uint32_t binary_data_len); + + +BSON_EXPORT(bool) +bson_vector_float32_const_view_init(bson_vector_float32_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len); + +BSON_EXPORT(bool) +bson_vector_packed_bit_view_init(bson_vector_packed_bit_view_t *view_out, + uint8_t *binary_data, + uint32_t binary_data_len); + +BSON_EXPORT(bool) +bson_vector_packed_bit_const_view_init(bson_vector_packed_bit_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len); + + +BSON_EXPORT(bool) +bson_vector_int8_view_from_iter(bson_vector_int8_view_t *view_out, bson_iter_t *iter); + +BSON_EXPORT(bool) +bson_vector_int8_const_view_from_iter(bson_vector_int8_const_view_t *view_out, const bson_iter_t *iter); + +BSON_EXPORT(bool) +bson_vector_float32_view_from_iter(bson_vector_float32_view_t *view_out, bson_iter_t *iter); + +BSON_EXPORT(bool) +bson_vector_float32_const_view_from_iter(bson_vector_float32_const_view_t *view_out, const bson_iter_t *iter); + +BSON_EXPORT(bool) +bson_vector_packed_bit_view_from_iter(bson_vector_packed_bit_view_t *view_out, bson_iter_t *iter); + +BSON_EXPORT(bool) +bson_vector_packed_bit_const_view_from_iter(bson_vector_packed_bit_const_view_t *view_out, const bson_iter_t *iter); + + +BSON_EXPORT(bool) +bson_array_builder_append_vector_int8_elements(struct _bson_array_builder_t *builder, + bson_vector_int8_const_view_t view); + +BSON_EXPORT(bool) +bson_array_builder_append_vector_float32_elements(struct _bson_array_builder_t *builder, + bson_vector_float32_const_view_t view); + +BSON_EXPORT(bool) +bson_array_builder_append_vector_packed_bit_elements(struct _bson_array_builder_t *builder, + bson_vector_packed_bit_const_view_t view); + +BSON_EXPORT(bool) +bson_array_builder_append_vector_elements(struct _bson_array_builder_t *builder, const bson_iter_t *iter); + + +BSON_EXPORT(bool) +bson_append_vector_int8_uninit( + bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_int8_view_t *view_out); + +#define BSON_APPEND_VECTOR_INT8_UNINIT(b, key, count, view) \ + bson_append_vector_int8_uninit(b, key, (int)strlen(key), count, view) + +BSON_EXPORT(bool) +bson_append_vector_float32_uninit( + bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_float32_view_t *view_out); + +#define BSON_APPEND_VECTOR_FLOAT32_UNINIT(b, key, count, view) \ + bson_append_vector_float32_uninit(b, key, (int)strlen(key), count, view) + +BSON_EXPORT(bool) +bson_append_vector_packed_bit_uninit( + bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_packed_bit_view_t *view_out); + +#define BSON_APPEND_VECTOR_PACKED_BIT_UNINIT(b, key, count, view) \ + bson_append_vector_packed_bit_uninit(b, key, (int)strlen(key), count, view) + + +BSON_EXPORT(bool) +bson_append_vector_int8_from_array( + bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error); + +#define BSON_APPEND_VECTOR_INT8_FROM_ARRAY(b, key, iter, err) \ + bson_append_vector_int8_from_array(b, key, (int)strlen(key), iter, err) + +BSON_EXPORT(bool) +bson_append_vector_float32_from_array( + bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error); + +#define BSON_APPEND_VECTOR_FLOAT32_FROM_ARRAY(b, key, iter, err) \ + bson_append_vector_float32_from_array(b, key, (int)strlen(key), iter, err) + +BSON_EXPORT(bool) +bson_append_vector_packed_bit_from_array( + bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error); + +#define BSON_APPEND_VECTOR_PACKED_BIT_FROM_ARRAY(b, key, iter, err) \ + bson_append_vector_packed_bit_from_array(b, key, (int)strlen(key), iter, err) + + +BSON_EXPORT(bool) +bson_append_array_from_vector_int8(bson_t *bson, const char *key, int key_length, bson_vector_int8_const_view_t view); + +#define BSON_APPEND_ARRAY_FROM_VECTOR_INT8(b, key, view) \ + bson_append_array_from_vector_int8(b, key, (int)strlen(key), view) + +BSON_EXPORT(bool) +bson_append_array_from_vector_float32(bson_t *bson, + const char *key, + int key_length, + bson_vector_float32_const_view_t view); + +#define BSON_APPEND_ARRAY_FROM_VECTOR_FLOAT32(b, key, view) \ + bson_append_array_from_vector_float32(b, key, (int)strlen(key), view) + +BSON_EXPORT(bool) +bson_append_array_from_vector_packed_bit(bson_t *bson, + const char *key, + int key_length, + bson_vector_packed_bit_const_view_t view); + +#define BSON_APPEND_ARRAY_FROM_VECTOR_PACKED_BIT(b, key, view) \ + bson_append_array_from_vector_packed_bit(b, key, (int)strlen(key), view) + + +static BSON_INLINE const int8_t * +bson_vector_int8_const_view_pointer(bson_vector_int8_const_view_t view) +{ + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + return (const int8_t *)(view.binary.data + BSON_VECTOR_HEADER_LEN); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END +} + +static BSON_INLINE int8_t * +bson_vector_int8_view_pointer(bson_vector_int8_view_t view) +{ + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + return (int8_t *)(view.binary.data + BSON_VECTOR_HEADER_LEN); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END +} + + +static BSON_INLINE uint32_t +bson_vector_int8_binary_data_length(size_t element_count) +{ + const size_t max_representable = (size_t)UINT32_MAX - (size_t)BSON_VECTOR_HEADER_LEN; + return element_count > max_representable ? 0u : (uint32_t)element_count + (uint32_t)BSON_VECTOR_HEADER_LEN; +} + +static BSON_INLINE uint32_t +bson_vector_float32_binary_data_length(size_t element_count) +{ + const size_t max_representable = ((size_t)UINT32_MAX - (size_t)BSON_VECTOR_HEADER_LEN) / sizeof(float); + return element_count > max_representable + ? 0u + : (uint32_t)element_count * sizeof(float) + (uint32_t)BSON_VECTOR_HEADER_LEN; +} + +static BSON_INLINE uint32_t +bson_vector_packed_bit_binary_data_length(size_t element_count) +{ + const size_t max_representable = + (size_t)BSON_MIN((uint64_t)SIZE_MAX, ((uint64_t)UINT32_MAX - (uint64_t)BSON_VECTOR_HEADER_LEN) * 8u); + return element_count > max_representable + ? 0u + : (uint32_t)(((uint64_t)element_count + 7u) / 8u) + (uint32_t)BSON_VECTOR_HEADER_LEN; +} + + +static BSON_INLINE size_t +bson_vector_int8_const_view_length(bson_vector_int8_const_view_t view) +{ + return view.binary.data_len - (uint32_t)BSON_VECTOR_HEADER_LEN; +} + +static BSON_INLINE size_t +bson_vector_int8_view_length(bson_vector_int8_view_t view) +{ + return bson_vector_int8_const_view_length(bson_vector_int8_view_as_const(view)); +} + +static BSON_INLINE size_t +bson_vector_float32_const_view_length(bson_vector_float32_const_view_t view) +{ + return (view.binary.data_len - (uint32_t)BSON_VECTOR_HEADER_LEN) / (uint32_t)sizeof(float); +} + +static BSON_INLINE size_t +bson_vector_float32_view_length(bson_vector_float32_view_t view) +{ + return bson_vector_float32_const_view_length(bson_vector_float32_view_as_const(view)); +} + +static BSON_INLINE size_t +bson_vector_packed_bit_const_view_length_bytes(bson_vector_packed_bit_const_view_t view) +{ + return view.binary.data_len - (uint32_t)BSON_VECTOR_HEADER_LEN; +} + +static BSON_INLINE size_t +bson_vector_packed_bit_view_length_bytes(bson_vector_packed_bit_view_t view) +{ + return bson_vector_packed_bit_const_view_length_bytes(bson_vector_packed_bit_view_as_const(view)); +} + +// Implementation detail, not part of documented API. +static BSON_INLINE size_t +bson_vector_padding_from_header_byte_1(uint8_t byte_1) +{ + return byte_1 & 7; +} + +static BSON_INLINE size_t +bson_vector_packed_bit_const_view_padding(bson_vector_packed_bit_const_view_t view) +{ + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + return bson_vector_padding_from_header_byte_1(view.binary.header_copy.bytes[1]); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END +} + +static BSON_INLINE size_t +bson_vector_packed_bit_view_padding(bson_vector_packed_bit_view_t view) +{ + return bson_vector_packed_bit_const_view_padding(bson_vector_packed_bit_view_as_const(view)); +} + +static BSON_INLINE size_t +bson_vector_packed_bit_const_view_length(bson_vector_packed_bit_const_view_t view) +{ + return bson_vector_packed_bit_const_view_length_bytes(view) * 8u - bson_vector_packed_bit_const_view_padding(view); +} + +static BSON_INLINE size_t +bson_vector_packed_bit_view_length(bson_vector_packed_bit_view_t view) +{ + return bson_vector_packed_bit_const_view_length(bson_vector_packed_bit_view_as_const(view)); +} + + +static BSON_INLINE bool +bson_vector_int8_const_view_read(bson_vector_int8_const_view_t view, + int8_t *BSON_RESTRICT values_out, + size_t element_count, + size_t vector_offset_elements) +{ + size_t length = bson_vector_int8_const_view_length(view); + if (BSON_LIKELY(vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + memcpy(values_out, bson_vector_int8_const_view_pointer(view) + vector_offset_elements, element_count); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + return true; + } else { + return false; + } +} + +static BSON_INLINE bool +bson_vector_int8_view_read(bson_vector_int8_view_t view, + int8_t *BSON_RESTRICT values_out, + size_t element_count, + uint32_t vector_offset_elements) +{ + return bson_vector_int8_const_view_read( + bson_vector_int8_view_as_const(view), values_out, element_count, vector_offset_elements); +} + +static BSON_INLINE bool +bson_vector_int8_view_write(bson_vector_int8_view_t view, + const int8_t *BSON_RESTRICT values, + size_t element_count, + size_t vector_offset_elements) +{ + size_t length = bson_vector_int8_view_length(view); + if (BSON_LIKELY(vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + memcpy(bson_vector_int8_view_pointer(view) + vector_offset_elements, values, element_count); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + return true; + } else { + return false; + } +} + + +BSON_STATIC_ASSERT2(float_is_float32, sizeof(float) == 4); + +static BSON_INLINE bool +bson_vector_float32_const_view_read(bson_vector_float32_const_view_t view, + float *BSON_RESTRICT values_out, + size_t element_count, + size_t vector_offset_elements) +{ + size_t length = bson_vector_float32_const_view_length(view); + if (BSON_LIKELY(vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { + size_t byte_offset = BSON_VECTOR_HEADER_LEN + vector_offset_elements * 4; + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN +#if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN + memcpy(values_out, view.binary.data + byte_offset, element_count * 4); +#else + size_t i; + for (i = 0; i < element_count; i++) { + float aligned_tmp; + memcpy(&aligned_tmp, view.binary.data + byte_offset + i * 4, 4); + values_out[i] = BSON_FLOAT_FROM_LE(aligned_tmp); + } +#endif + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + return true; + } else { + return false; + } +} + +static BSON_INLINE bool +bson_vector_float32_view_read(bson_vector_float32_view_t view, + float *BSON_RESTRICT values_out, + size_t element_count, + size_t vector_offset_elements) +{ + return bson_vector_float32_const_view_read( + bson_vector_float32_view_as_const(view), values_out, element_count, vector_offset_elements); +} + +static BSON_INLINE bool +bson_vector_float32_view_write(bson_vector_float32_view_t view, + const float *BSON_RESTRICT values, + size_t element_count, + size_t vector_offset_elements) +{ + size_t length = bson_vector_float32_view_length(view); + if (BSON_LIKELY(vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { + size_t byte_offset = BSON_VECTOR_HEADER_LEN + vector_offset_elements * 4; + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN +#if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN + memcpy(view.binary.data + byte_offset, values, element_count * 4); +#else + size_t i; + for (i = 0; i < element_count; i++) { + float aligned_tmp = BSON_FLOAT_TO_LE(values[i]); + memcpy(view.binary.data + byte_offset + i * 4, &aligned_tmp, 4); + } +#endif + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + return true; + } else { + return false; + } +} + + +static BSON_INLINE bool +bson_vector_packed_bit_const_view_read_packed(bson_vector_packed_bit_const_view_t view, + uint8_t *BSON_RESTRICT packed_values_out, + size_t byte_count, + size_t vector_offset_bytes) +{ + size_t length_bytes = bson_vector_packed_bit_const_view_length_bytes(view); + if (BSON_LIKELY(vector_offset_bytes <= length_bytes && byte_count <= length_bytes - vector_offset_bytes)) { + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + memcpy(packed_values_out, view.binary.data + BSON_VECTOR_HEADER_LEN + vector_offset_bytes, byte_count); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + return true; + } else { + return false; + } +} + +static BSON_INLINE bool +bson_vector_packed_bit_view_read_packed(bson_vector_packed_bit_view_t view, + uint8_t *BSON_RESTRICT packed_values_out, + size_t byte_count, + size_t vector_offset_bytes) +{ + return bson_vector_packed_bit_const_view_read_packed( + bson_vector_packed_bit_view_as_const(view), packed_values_out, byte_count, vector_offset_bytes); +} + +static BSON_INLINE bool +bson_vector_packed_bit_view_write_packed(bson_vector_packed_bit_view_t view, + const uint8_t *BSON_RESTRICT packed_values, + size_t byte_count, + size_t vector_offset_bytes) +{ + size_t length_bytes = bson_vector_packed_bit_view_length_bytes(view); + if (BSON_LIKELY(vector_offset_bytes <= length_bytes && byte_count <= length_bytes - vector_offset_bytes)) { + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + if (byte_count == length_bytes - vector_offset_bytes && byte_count >= 1u) { + // This write touches the last byte in the vector: + // special-case that byte so we can ensure unused bits remain set to zero. + size_t other_bytes = byte_count - 1u; + memcpy(view.binary.data + BSON_VECTOR_HEADER_LEN + vector_offset_bytes, packed_values, other_bytes); + view.binary.data[BSON_VECTOR_HEADER_LEN + vector_offset_bytes + other_bytes] = + (UINT8_C(0xFF) << bson_vector_packed_bit_view_padding(view)) & packed_values[other_bytes]; + } else { + memcpy(view.binary.data + BSON_VECTOR_HEADER_LEN + vector_offset_bytes, packed_values, byte_count); + } + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + return true; + } else { + return false; + } +} + + +static BSON_INLINE bool +bson_vector_packed_bit_const_view_unpack_bool(bson_vector_packed_bit_const_view_t view, + bool *BSON_RESTRICT unpacked_values_out, + size_t element_count, + size_t vector_offset_elements) +{ + size_t length = bson_vector_packed_bit_const_view_length(view); + if (BSON_LIKELY(vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { + size_t i; + for (i = 0; i < element_count; i++) { + size_t element_index = vector_offset_elements + i; + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + uint8_t packed_byte = view.binary.data[BSON_VECTOR_HEADER_LEN + (element_index >> 3)]; + unpacked_values_out[i] = 0 != (packed_byte & ((uint8_t)0x80 >> (element_index & 7))); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + } + return true; + } else { + return false; + } +} + +static BSON_INLINE bool +bson_vector_packed_bit_view_unpack_bool(bson_vector_packed_bit_view_t view, + bool *BSON_RESTRICT unpacked_values_out, + size_t element_count, + size_t vector_offset_elements) +{ + return bson_vector_packed_bit_const_view_unpack_bool( + bson_vector_packed_bit_view_as_const(view), unpacked_values_out, element_count, vector_offset_elements); +} + +static BSON_INLINE bool +bson_vector_packed_bit_view_pack_bool(bson_vector_packed_bit_view_t view, + const bool *BSON_RESTRICT unpacked_values, + size_t element_count, + size_t vector_offset_elements) +{ + size_t length = bson_vector_packed_bit_view_length(view); + if (BSON_LIKELY(vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { + while (element_count > 0) { + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + uint8_t *BSON_RESTRICT packed_byte = BSON_VECTOR_HEADER_LEN + (vector_offset_elements >> 3) + view.binary.data; + if (element_count >= 8 && (vector_offset_elements & 7) == 0) { + uint8_t complete_byte = 0; + unsigned i; + for (i = 0; i < 8; i++) { + complete_byte |= unpacked_values[i] ? ((uint8_t)0x80 >> i) : 0; + } + *packed_byte = complete_byte; + unpacked_values += 8; + vector_offset_elements += 8; + element_count -= 8; + } else { + uint8_t mask = (uint8_t)0x80 >> (vector_offset_elements & 7); + *packed_byte = (*packed_byte & ~mask) | (*unpacked_values ? mask : 0); + unpacked_values++; + vector_offset_elements++; + element_count--; + } + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + } + return true; + } else { + return false; + } +} + + +BSON_END_DECLS + +#endif /* BSON_VECTOR_H */ diff --git a/bsonjs/bson/bson-version-functions.c b/bsonjs/bson/bson-version-functions.c index 90406ed..4131bc4 100644 --- a/bsonjs/bson/bson-version-functions.c +++ b/bsonjs/bson/bson-version-functions.c @@ -1,5 +1,5 @@ /* - * Copyright 2015 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,9 @@ * limitations under the License. */ - -#include #include +#include /** * bson_get_major_version: @@ -25,19 +24,18 @@ * Helper function to return the runtime major version of the library. */ int -bson_get_major_version (void) +bson_get_major_version(void) { return BSON_MAJOR_VERSION; } - /** * bson_get_minor_version: * * Helper function to return the runtime minor version of the library. */ int -bson_get_minor_version (void) +bson_get_minor_version(void) { return BSON_MINOR_VERSION; } @@ -48,7 +46,7 @@ bson_get_minor_version (void) * Helper function to return the runtime micro version of the library. */ int -bson_get_micro_version (void) +bson_get_micro_version(void) { return BSON_MICRO_VERSION; } @@ -59,7 +57,7 @@ bson_get_micro_version (void) * Helper function to return the runtime string version of the library. */ const char * -bson_get_version (void) +bson_get_version(void) { return BSON_VERSION_S; } @@ -71,7 +69,7 @@ bson_get_version (void) * version. */ bool -bson_check_version (int required_major, int required_minor, int required_micro) +bson_check_version(int required_major, int required_minor, int required_micro) { - return BSON_CHECK_VERSION (required_major, required_minor, required_micro); + return BSON_CHECK_VERSION(required_major, required_minor, required_micro); } diff --git a/bsonjs/bson/bson-version-functions.h b/bsonjs/bson/bson-version-functions.h index 923dcf0..de22c15 100644 --- a/bsonjs/bson/bson-version-functions.h +++ b/bsonjs/bson/bson-version-functions.h @@ -1,5 +1,5 @@ /* - * Copyright 2015 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,16 +25,16 @@ BSON_BEGIN_DECLS -BSON_EXPORT (int) -bson_get_major_version (void); -BSON_EXPORT (int) -bson_get_minor_version (void); -BSON_EXPORT (int) -bson_get_micro_version (void); -BSON_EXPORT (const char *) -bson_get_version (void); -BSON_EXPORT (bool) -bson_check_version (int required_major, int required_minor, int required_micro); +BSON_EXPORT(int) +bson_get_major_version(void); +BSON_EXPORT(int) +bson_get_minor_version(void); +BSON_EXPORT(int) +bson_get_micro_version(void); +BSON_EXPORT(const char *) +bson_get_version(void); +BSON_EXPORT(bool) +bson_check_version(int required_major, int required_minor, int required_micro); BSON_END_DECLS diff --git a/bsonjs/bson/bson-writer.c b/bsonjs/bson/bson-writer.c index d547698..f2953c5 100644 --- a/bsonjs/bson/bson-writer.c +++ b/bsonjs/bson/bson-writer.c @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,10 @@ */ -#include #include +#include + struct _bson_writer_t { bool ready; @@ -58,15 +59,15 @@ struct _bson_writer_t { */ bson_writer_t * -bson_writer_new (uint8_t **buf, /* IN */ - size_t *buflen, /* IN */ - size_t offset, /* IN */ - bson_realloc_func realloc_func, /* IN */ - void *realloc_func_ctx) /* IN */ +bson_writer_new(uint8_t **buf, /* IN */ + size_t *buflen, /* IN */ + size_t offset, /* IN */ + bson_realloc_func realloc_func, /* IN */ + void *realloc_func_ctx) /* IN */ { bson_writer_t *writer; - writer = BSON_ALIGNED_ALLOC0 (bson_writer_t); + writer = BSON_ALIGNED_ALLOC0(bson_writer_t); writer->buf = buf; writer->buflen = buflen; writer->offset = offset; @@ -97,9 +98,9 @@ bson_writer_new (uint8_t **buf, /* IN */ */ void -bson_writer_destroy (bson_writer_t *writer) /* IN */ +bson_writer_destroy(bson_writer_t *writer) /* IN */ { - bson_free (writer); + bson_free(writer); } @@ -126,7 +127,7 @@ bson_writer_destroy (bson_writer_t *writer) /* IN */ */ size_t -bson_writer_get_length (bson_writer_t *writer) /* IN */ +bson_writer_get_length(bson_writer_t *writer) /* IN */ { return writer->offset + writer->b.len; } @@ -156,35 +157,35 @@ bson_writer_get_length (bson_writer_t *writer) /* IN */ */ bool -bson_writer_begin (bson_writer_t *writer, /* IN */ - bson_t **bson) /* OUT */ +bson_writer_begin(bson_writer_t *writer, /* IN */ + bson_t **bson) /* OUT */ { bson_impl_alloc_t *b; bool grown = false; - BSON_ASSERT (writer); - BSON_ASSERT (writer->ready); - BSON_ASSERT (bson); + BSON_ASSERT(writer); + BSON_ASSERT(writer->ready); + BSON_ASSERT(bson); writer->ready = false; - memset (&writer->b, 0, sizeof (bson_t)); + memset(&writer->b, 0, sizeof(bson_t)); - b = (bson_impl_alloc_t *) &writer->b; - b->flags = BSON_FLAG_STATIC | BSON_FLAG_NO_FREE; + b = (bson_impl_alloc_t *)&writer->b; + b->flags = BSON_FLAG_NO_FREE_OBJECT | BSON_FLAG_NO_FREE_DATA; b->len = 5; b->parent = NULL; - b->buf = writer->buf; - b->buflen = writer->buflen; + b->indirect_buffer = writer->buf; + b->indirect_buflen = writer->buflen; b->offset = writer->offset; - b->alloc = NULL; - b->alloclen = 0; + b->own_buffer = NULL; + b->own_buflen = 0; b->realloc = writer->realloc_func; b->realloc_func_ctx = writer->realloc_func_ctx; while ((writer->offset + writer->b.len) > *writer->buflen) { if (!writer->realloc_func) { - memset (&writer->b, 0, sizeof (bson_t)); + memset(&writer->b, 0, sizeof(bson_t)); writer->ready = true; return false; } @@ -198,10 +199,10 @@ bson_writer_begin (bson_writer_t *writer, /* IN */ } if (grown) { - *writer->buf = writer->realloc_func (*writer->buf, *writer->buflen, writer->realloc_func_ctx); + *writer->buf = writer->realloc_func(*writer->buf, *writer->buflen, writer->realloc_func_ctx); } - memset ((*writer->buf) + writer->offset + 1, 0, 5); + memset((*writer->buf) + writer->offset + 1, 0, 5); (*writer->buf)[writer->offset] = 5; *bson = &writer->b; @@ -227,13 +228,13 @@ bson_writer_begin (bson_writer_t *writer, /* IN */ */ void -bson_writer_end (bson_writer_t *writer) /* IN */ +bson_writer_end(bson_writer_t *writer) /* IN */ { - BSON_ASSERT (writer); - BSON_ASSERT (!writer->ready); + BSON_ASSERT(writer); + BSON_ASSERT(!writer->ready); writer->offset += writer->b.len; - memset (&writer->b, 0, sizeof (bson_t)); + memset(&writer->b, 0, sizeof(bson_t)); writer->ready = true; } @@ -258,12 +259,12 @@ bson_writer_end (bson_writer_t *writer) /* IN */ */ void -bson_writer_rollback (bson_writer_t *writer) /* IN */ +bson_writer_rollback(bson_writer_t *writer) /* IN */ { - BSON_ASSERT (writer); + BSON_ASSERT(writer); if (writer->b.len) { - memset (&writer->b, 0, sizeof (bson_t)); + memset(&writer->b, 0, sizeof(bson_t)); } writer->ready = true; diff --git a/bsonjs/bson/bson-writer.h b/bsonjs/bson/bson-writer.h index dd7d898..64fecce 100644 --- a/bsonjs/bson/bson-writer.h +++ b/bsonjs/bson/bson-writer.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,17 +16,16 @@ #include - #ifndef BSON_WRITER_H #define BSON_WRITER_H +#include +#include -#include "bson.h" - +#include BSON_BEGIN_DECLS - /** * bson_writer_t: * @@ -40,22 +39,19 @@ BSON_BEGIN_DECLS */ typedef struct _bson_writer_t bson_writer_t; - -BSON_EXPORT (bson_writer_t *) -bson_writer_new (uint8_t **buf, size_t *buflen, size_t offset, bson_realloc_func realloc_func, void *realloc_func_ctx); -BSON_EXPORT (void) -bson_writer_destroy (bson_writer_t *writer); -BSON_EXPORT (size_t) -bson_writer_get_length (bson_writer_t *writer); -BSON_EXPORT (bool) -bson_writer_begin (bson_writer_t *writer, bson_t **bson); -BSON_EXPORT (void) -bson_writer_end (bson_writer_t *writer); -BSON_EXPORT (void) -bson_writer_rollback (bson_writer_t *writer); - +BSON_EXPORT(bson_writer_t *) +bson_writer_new(uint8_t **buf, size_t *buflen, size_t offset, bson_realloc_func realloc_func, void *realloc_func_ctx); +BSON_EXPORT(void) +bson_writer_destroy(bson_writer_t *writer); +BSON_EXPORT(size_t) +bson_writer_get_length(bson_writer_t *writer); +BSON_EXPORT(bool) +bson_writer_begin(bson_writer_t *writer, bson_t **bson); +BSON_EXPORT(void) +bson_writer_end(bson_writer_t *writer); +BSON_EXPORT(void) +bson_writer_rollback(bson_writer_t *writer); BSON_END_DECLS - #endif /* BSON_WRITER_H */ diff --git a/bsonjs/bson/bson.c b/bsonjs/bson/bson.c index adc5ee2..b2fe9aa 100644 --- a/bsonjs/bson/bson.c +++ b/bsonjs/bson/bson.c @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,74 +15,59 @@ */ -#include "bson.h" -#include -#include -#include -#include -#include - -#include "common-b64-private.h" - -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include -#ifndef BSON_MAX_RECURSION -#define BSON_MAX_RECURSION 200 -#endif +#include +#include +#include -typedef enum { - BSON_VALIDATE_PHASE_START, - BSON_VALIDATE_PHASE_TOP, - BSON_VALIDATE_PHASE_LF_REF_KEY, - BSON_VALIDATE_PHASE_LF_REF_UTF8, - BSON_VALIDATE_PHASE_LF_ID_KEY, - BSON_VALIDATE_PHASE_LF_DB_KEY, - BSON_VALIDATE_PHASE_LF_DB_UTF8, - BSON_VALIDATE_PHASE_NOT_DBREF, -} bson_validate_phase_t; +#include +#include /* - * Structures. + * Globals. */ -typedef struct { - bson_validate_flags_t flags; - ssize_t err_offset; - bson_validate_phase_t phase; - bson_error_t error; -} bson_validate_state_t; - - -typedef struct { - uint32_t count; - bool keys; - ssize_t *err_offset; - uint32_t depth; - bson_string_t *str; - bson_json_mode_t mode; - int32_t max_len; - bool max_len_reached; -} bson_json_state_t; - +static const uint8_t gZero = 0; /* - * Forward declarations. + *-------------------------------------------------------------------------- + * + * _bson_round_up_alloc_size -- + * + * Given a potential allocation length in bytes, round up to the + * next power of two without exceeding BSON_MAX_SIZE. + * + * Returns: + * If the input is <= BSON_MAX_SIZE, returns a value >= the input + * and still <= BSON_MAX_SIZE. If the input was greater than + * BSON_MAX_SIZE, it is returned unmodified. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- */ -static bool -_bson_as_json_visit_array (const bson_iter_t *iter, const char *key, const bson_t *v_array, void *data); -static bool -_bson_as_json_visit_document (const bson_iter_t *iter, const char *key, const bson_t *v_document, void *data); -static char * -_bson_as_json_visit_all ( - const bson_t *bson, size_t *length, bson_json_mode_t mode, int32_t max_len, bool is_outermost_array); -/* - * Globals. - */ -static const uint8_t gZero; +static BSON_INLINE size_t +_bson_round_up_alloc_size(size_t size) +{ + if (size <= BSON_MAX_SIZE) { + size_t power_of_two = bson_next_power_of_two(size); + return BSON_MIN(power_of_two, BSON_MAX_SIZE); + } else { + return size; + } +} /* *-------------------------------------------------------------------------- @@ -103,34 +88,34 @@ static const uint8_t gZero; */ static bool -_bson_impl_inline_grow (bson_impl_inline_t *impl, /* IN */ - size_t size) /* IN */ +_bson_impl_inline_grow(bson_impl_inline_t *impl, /* IN */ + uint32_t grow_size) /* IN */ { - bson_impl_alloc_t *alloc = (bson_impl_alloc_t *) impl; + bson_impl_alloc_t *alloc = (bson_impl_alloc_t *)impl; uint8_t *data; - size_t req; - if (((size_t) impl->len + size) <= sizeof impl->data) { + MONGOC_DEBUG_ASSERT((size_t)impl->len <= BSON_MAX_SIZE); + MONGOC_DEBUG_ASSERT((size_t)grow_size <= BSON_MAX_SIZE); + size_t req = (size_t)impl->len + (size_t)grow_size; + + if (req <= sizeof impl->data) { return true; } - req = bson_next_power_of_two (impl->len + size); + req = _bson_round_up_alloc_size(req); if (req <= BSON_MAX_SIZE) { - data = bson_malloc (req); + data = bson_malloc(req); - memcpy (data, impl->data, impl->len); -#ifdef BSON_MEMCHECK - bson_free (impl->canary); -#endif - alloc->flags &= ~BSON_FLAG_INLINE; + memcpy(data, impl->data, impl->len); + alloc->flags &= ~BSON_FLAG_INLINE_DATA; alloc->parent = NULL; alloc->depth = 0; - alloc->buf = &alloc->alloc; - alloc->buflen = &alloc->alloclen; + alloc->indirect_buffer = NULL; + alloc->indirect_buflen = NULL; alloc->offset = 0; - alloc->alloc = data; - alloc->alloclen = req; + alloc->own_buffer = data; + alloc->own_buflen = req; alloc->realloc = bson_realloc_ctx; alloc->realloc_func_ctx = NULL; @@ -146,11 +131,12 @@ _bson_impl_inline_grow (bson_impl_inline_t *impl, /* IN */ * * _bson_impl_alloc_grow -- * - * Document growth implementation for documents containing malloc - * based buffers. + * Document growth implementation for non-inline documents, possibly + * containing a reallocatable buffer. * * Returns: - * true if successful; otherwise false indicating BSON_MAX_SIZE overflow. + * true if successful; otherwise false indicating BSON_MAX_SIZE overflow + * or an attempt to grow a buffer with no realloc implementation. * * Side effects: * None. @@ -159,26 +145,33 @@ _bson_impl_inline_grow (bson_impl_inline_t *impl, /* IN */ */ static bool -_bson_impl_alloc_grow (bson_impl_alloc_t *impl, /* IN */ - size_t size) /* IN */ +_bson_impl_alloc_grow(bson_impl_alloc_t *impl, /* IN */ + uint32_t grow_size) /* IN */ { - size_t req; - - /* - * Determine how many bytes we need for this document in the buffer + /* Determine how many bytes we need for this document in the buffer * including necessary trailing bytes for parent documents. + * + * On size assumptions: the previous grow operation has already checked + * (len + offset + previous_depth) against BSON_MAX_SIZE. Current depth can be at most (previous_depth + 1). The + * caller has checked grow_size against BSON_MAX_SIZE. On the smallest (32-bit) supported size_t, we can still add + * these maximum values (2x BSON_MAX_SIZE, 1 additional byte of depth) without arithmetic overflow. */ - req = (impl->offset + impl->len + size + impl->depth); + MONGOC_DEBUG_ASSERT((uint64_t)impl->len + (uint64_t)impl->offset + (uint64_t)impl->depth <= (uint64_t)BSON_MAX_SIZE); + MONGOC_DEBUG_ASSERT((size_t)grow_size <= BSON_MAX_SIZE); + size_t req = impl->offset + (size_t)impl->len + (size_t)grow_size + (size_t)impl->depth; + + uint8_t **const buf_to_grow = impl->indirect_buffer ? impl->indirect_buffer : &impl->own_buffer; + size_t *const sz_to_grow = impl->indirect_buffer ? impl->indirect_buflen : &impl->own_buflen; - if (req <= *impl->buflen) { + if (req <= *sz_to_grow) { return true; } - req = bson_next_power_of_two (req); + req = _bson_round_up_alloc_size(req); if ((req <= BSON_MAX_SIZE) && impl->realloc) { - *impl->buf = impl->realloc (*impl->buf, req, impl->realloc_func_ctx); - *impl->buflen = req; + *buf_to_grow = impl->realloc(*buf_to_grow, req, impl->realloc_func_ctx); + *sz_to_grow = req; return true; } @@ -191,11 +184,16 @@ _bson_impl_alloc_grow (bson_impl_alloc_t *impl, /* IN */ * * _bson_grow -- * - * Grows the bson_t structure to be large enough to contain @size - * bytes. + * Grows the bson_t structure to be large enough to contain @grow_size + * bytes in addition to its current content. + * + * The caller is responsible for ensuring @grow_size itself is not + * above BSON_MAX_SIZE, but a final determination of overflow status + * can't be made until we are inside _bson_impl_*_grow(). * * Returns: - * true if successful, false if the size would overflow. + * true if successful, false if the size would overflow or the buffer + * needs to grow but does not support reallocation. * * Side effects: * None. @@ -204,14 +202,16 @@ _bson_impl_alloc_grow (bson_impl_alloc_t *impl, /* IN */ */ static bool -_bson_grow (bson_t *bson, /* IN */ - uint32_t size) /* IN */ +_bson_grow(bson_t *bson, /* IN */ + uint32_t grow_size) /* IN */ { - if ((bson->flags & BSON_FLAG_INLINE)) { - return _bson_impl_inline_grow ((bson_impl_inline_t *) bson, size); + BSON_ASSERT((size_t)grow_size <= BSON_MAX_SIZE); + + if ((bson->flags & BSON_FLAG_INLINE_DATA)) { + return _bson_impl_inline_grow((bson_impl_inline_t *)bson, grow_size); } - return _bson_impl_alloc_grow ((bson_impl_alloc_t *) bson, size); + return _bson_impl_alloc_grow((bson_impl_alloc_t *)bson, grow_size); } @@ -233,13 +233,14 @@ _bson_grow (bson_t *bson, /* IN */ */ static BSON_INLINE uint8_t * -_bson_data (const bson_t *bson) /* IN */ +_bson_data(const bson_t *bson) /* IN */ { - if ((bson->flags & BSON_FLAG_INLINE)) { - return ((bson_impl_inline_t *) bson)->data; + if ((bson->flags & BSON_FLAG_INLINE_DATA)) { + return ((bson_impl_inline_t *)bson)->data; } else { - bson_impl_alloc_t *impl = (bson_impl_alloc_t *) bson; - return (*impl->buf) + impl->offset; + bson_impl_alloc_t *impl = (bson_impl_alloc_t *)bson; + uint8_t *base = impl->indirect_buffer ? *impl->indirect_buffer : impl->own_buffer; + return base + impl->offset; } } @@ -263,172 +264,98 @@ _bson_data (const bson_t *bson) /* IN */ */ static BSON_INLINE void -_bson_encode_length (bson_t *bson) /* IN */ -{ -#if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN - memcpy (_bson_data (bson), &bson->len, sizeof (bson->len)); -#else - uint32_t length_le = BSON_UINT32_TO_LE (bson->len); - memcpy (_bson_data (bson), &length_le, sizeof (length_le)); -#endif -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_append_va -- - * - * Appends the length,buffer pairs to the bson_t. @n_bytes is an - * optimization to perform one array growth rather than many small - * growths. - * - * @bson: A bson_t - * @n_bytes: The number of bytes to append to the document. - * @n_pairs: The number of length,buffer pairs. - * @first_len: Length of first buffer. - * @first_data: First buffer. - * @args: va_list of additional tuples. - * - * Returns: - * true if the bytes were appended successfully. - * false if it bson would overflow BSON_MAX_SIZE. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static BSON_INLINE bool -_bson_append_va (bson_t *bson, /* IN */ - uint32_t n_bytes, /* IN */ - uint32_t n_pairs, /* IN */ - uint32_t first_len, /* IN */ - const uint8_t *first_data, /* IN */ - va_list args) /* IN */ -{ - const uint8_t *data; - uint32_t data_len; - uint8_t *buf; - - BSON_ASSERT (!(bson->flags & BSON_FLAG_IN_CHILD)); - BSON_ASSERT (!(bson->flags & BSON_FLAG_RDONLY)); - - if (BSON_UNLIKELY (!_bson_grow (bson, n_bytes))) { - return false; - } - - data = first_data; - data_len = first_len; - - buf = _bson_data (bson) + bson->len - 1; - - do { - n_pairs--; - /* data may be NULL if data_len is 0. memcpy is not safe to call with - * NULL. */ - if (BSON_LIKELY (data_len != 0 && data != NULL)) { - memcpy (buf, data, data_len); - bson->len += data_len; - buf += data_len; - } else if (BSON_UNLIKELY (data_len != 0 && data == NULL)) { - /* error, user appending NULL with non-zero length. */ - return false; - } - - if (n_pairs) { - data_len = va_arg (args, uint32_t); - data = va_arg (args, const uint8_t *); - } - } while (n_pairs); - - _bson_encode_length (bson); - - *buf = '\0'; - - return true; -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_append -- - * - * Variadic function to append length,buffer pairs to a bson_t. If the - * append would cause the bson_t to overflow a 32-bit length, it will - * return false and no append will have occurred. - * - * Parameters: - * @bson: A bson_t. - * @n_pairs: Number of length,buffer pairs. - * @n_bytes: the total number of bytes being appended. - * @first_len: Length of first buffer. - * @first_data: First buffer. - * - * Returns: - * true if successful; otherwise false indicating BSON_MAX_SIZE overflow. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ +_bson_encode_length(bson_t *bson) /* IN */ +{ + mlib_write_u32le(_bson_data(bson), bson->len); +} + + +typedef struct _bson_append_bytes_arg { + const uint8_t *bytes; // Optional. + uint32_t length; // > 0. +} _bson_append_bytes_arg; + +typedef struct _bson_append_bytes_list { + _bson_append_bytes_arg args[8]; // Arbitrary length: just needs to be large enough. + _bson_append_bytes_arg *current; // "Insert"/"End" pointer. + uint32_t n_bytes; // Total bytes to be appended. +} _bson_append_bytes_list; + +// To support unchecked cast from non-negative `int` to `size_t`. +BSON_STATIC_ASSERT2(size_t_gte_int, SIZE_MAX >= INT_MAX); + +// To support unchecked cast from `uint32_t` to `size_t`. +BSON_STATIC_ASSERT2(size_t_gte_uint32_t, SIZE_MAX >= UINT32_MAX); + +// Support largest _bson_impl_alloc_grow on smallest size_t +BSON_STATIC_ASSERT2(max_alloc_grow_fits_min_sizet, (uint64_t)BSON_MAX_SIZE * 2u + 1u <= (uint64_t)UINT32_MAX); + +// Declare local state with the identifier `ident`. +#define BSON_APPEND_BYTES_LIST_DECLARE(ident) \ + _bson_append_bytes_list ident = {.current = (ident).args, .n_bytes = 0u}; \ + ((void)0) + +// Add a bytes+length pair only if `_length > 0`. +// Append failure if `n_bytes` will exceed BSON max size. +#define BSON_APPEND_BYTES_ADD_ARGUMENT(_list, _bytes, _length) \ + mlib_diagnostic_push(); \ + mlib_disable_constant_conditional_expression_warnings(); \ + if (BSON_UNLIKELY((_length) > BSON_MAX_SIZE - (_list).n_bytes)) { \ + mlib_diagnostic_pop(); \ + goto append_failure; \ + } else if ((_length) > 0) { \ + *(_list).current++ = (_bson_append_bytes_arg){ \ + .bytes = (const uint8_t *)(_bytes), \ + .length = (_length), \ + }; \ + (_list).n_bytes += (_length); \ + } else \ + ((void)0) + +// Add a UTF-8 string only if no embedded null bytes are present. +// Uses `strlen (_key)` when `_key_len < 0`, otherwise uses `_key_len`. +#define BSON_APPEND_BYTES_ADD_CHECKED_STRING(_list, _key, _key_len) \ + uint32_t BSON_CONCAT(key_ulen_, __LINE__); \ + if ((_key_len) < 0) { \ + const size_t key_zulen = strlen((_key)); \ + if (BSON_UNLIKELY(key_zulen > UINT32_MAX)) { \ + goto append_failure; \ + } \ + BSON_CONCAT(key_ulen_, __LINE__) = (uint32_t)key_zulen; \ + } else { \ + const size_t key_zulen = (size_t)(_key_len); \ + if (BSON_UNLIKELY(key_zulen > UINT32_MAX)) { \ + goto append_failure; \ + } /* Necessary to validate embedded NULL is not present in key. */ \ + else if (memchr((_key), '\0', key_zulen) != NULL) { \ + goto append_failure; \ + } else { \ + BSON_CONCAT(key_ulen_, __LINE__) = (uint32_t)key_zulen; \ + } \ + } \ + BSON_APPEND_BYTES_ADD_ARGUMENT((_list), (_key), BSON_CONCAT(key_ulen_, __LINE__)) + +// Apply the list of arguments to be appended to `_bson`. +// Append failure if adding `_list.n_bytes` will exceed BSON max size. +#define BSON_APPEND_BYTES_APPLY_ARGUMENTS(_bson, _list) \ + if (BSON_UNLIKELY((_list).n_bytes > BSON_MAX_SIZE - (_bson)->len)) { \ + goto append_failure; \ + } else if (BSON_UNLIKELY(!_bson_grow((_bson), (_list).n_bytes))) { \ + goto append_failure; \ + } else { \ + uint8_t *data = _bson_data((_bson)) + ((_bson)->len - 1u); \ + for (const _bson_append_bytes_arg *arg = (_list).args; arg != (_list).current; ++arg) { \ + if (arg->bytes) { \ + memcpy(data, arg->bytes, arg->length); \ + } \ + (_bson)->len += arg->length; \ + data += arg->length; \ + } \ + _bson_encode_length((_bson)); \ + data[0] = '\0'; \ + } \ + ((void)0) -static bool -_bson_append (bson_t *bson, /* IN */ - uint32_t n_pairs, /* IN */ - uint32_t n_bytes, /* IN */ - uint32_t first_len, /* IN */ - const uint8_t *first_data, /* IN */ - ...) -{ - va_list args; - bool ok; - - BSON_ASSERT (n_pairs); - BSON_ASSERT (first_len); - BSON_ASSERT (first_data); - - /* - * Check to see if this append would overflow 32-bit signed integer. I know - * what you're thinking. BSON uses a signed 32-bit length field? Yeah. It - * does. - */ - if (BSON_UNLIKELY (n_bytes > (BSON_MAX_SIZE - bson->len))) { - return false; - } - - va_start (args, first_data); - ok = _bson_append_va (bson, n_bytes, n_pairs, first_len, first_data, args); - va_end (args); - - return ok; -} - -static BSON_INLINE bool -_string_contains_null (const char *str, size_t len) -{ - for (; len; ++str, --len) { - if (*str == 0) { - return true; - } - } - return false; -} - -#define HANDLE_KEY_LENGTH(key, key_length) \ - do { \ - if (key_length < 0) { \ - key_length = (int) strlen (key); \ - } else { \ - /* Necessary to validate embedded NULL is not present in key. */ \ - if (_string_contains_null (key, key_length)) { \ - return false; \ - } \ - } \ - } while (0) /* *-------------------------------------------------------------------------- @@ -453,46 +380,61 @@ _string_contains_null (const char *str, size_t len) */ static bool -_bson_append_bson_begin (bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - bson_type_t child_type, /* IN */ - bson_t *child) /* OUT */ +_bson_append_bson_begin(bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + bson_type_t child_type, /* IN */ + bson_t *child) /* OUT */ { - const uint8_t type = child_type; - const uint8_t empty[5] = {5}; - bson_impl_alloc_t *aparent = (bson_impl_alloc_t *) bson; - bson_impl_alloc_t *achild = (bson_impl_alloc_t *) child; + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + BSON_ASSERT_PARAM(child); - BSON_ASSERT (!(bson->flags & BSON_FLAG_RDONLY)); - BSON_ASSERT (!(bson->flags & BSON_FLAG_IN_CHILD)); - BSON_ASSERT (key); - BSON_ASSERT ((child_type == BSON_TYPE_DOCUMENT) || (child_type == BSON_TYPE_ARRAY)); - BSON_ASSERT (child); + BSON_ASSERT(!(bson->flags & BSON_FLAG_RDONLY)); + BSON_ASSERT(!(bson->flags & BSON_FLAG_IN_CHILD)); + BSON_ASSERT((child_type == BSON_TYPE_DOCUMENT) || (child_type == BSON_TYPE_ARRAY)); - HANDLE_KEY_LENGTH (key, key_length); + { + BSON_APPEND_BYTES_LIST_DECLARE(args); - /* - * If the parent is an inline bson_t, then we need to convert - * it to a heap allocated buffer. This makes extending buffers - * of child bson documents much simpler logic, as they can just - * realloc the *buf pointer. - */ - if ((bson->flags & BSON_FLAG_INLINE)) { - BSON_ASSERT (bson->len <= 120); - if (!_bson_grow (bson, 128 - bson->len)) { - return false; + const uint8_t type = (uint8_t)child_type; + + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + + /* + * If the parent is an inline bson_t, then we need to convert + * it to a heap allocated buffer. This makes extending buffers + * of child bson documents much simpler logic, as they can just + * realloc the *buf pointer. + */ + if ((bson->flags & BSON_FLAG_INLINE_DATA)) { + BSON_ASSERT(bson->len <= 120); + if (!_bson_grow(bson, 128 - bson->len)) { + return false; + } + BSON_ASSERT(!(bson->flags & BSON_FLAG_INLINE_DATA)); } - BSON_ASSERT (!(bson->flags & BSON_FLAG_INLINE)); - } - /* - * Append the type and key for the field. - */ - if (!_bson_append (bson, 4, (1 + key_length + 1 + 5), 1, &type, key_length, key, 1, &gZero, 5, empty)) { + const uint8_t empty[5] = {5}; + + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &empty, sizeof(empty)); + + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); + + goto append_success; + + append_failure: return false; } +append_success: + ((void)0); + + bson_impl_alloc_t *aparent = (bson_impl_alloc_t *)bson; + bson_impl_alloc_t *achild = (bson_impl_alloc_t *)child; + /* * Mark the document as working on a child document so that no * further modifications can happen until the caller has called @@ -505,21 +447,21 @@ _bson_append_bson_begin (bson_t *bson, /* IN */ * buffers. This allows us to realloc directly from the child without * walking up to the parent bson_t. */ - achild->flags = (BSON_FLAG_CHILD | BSON_FLAG_NO_FREE | BSON_FLAG_STATIC); + achild->flags = (BSON_FLAG_CHILD | BSON_FLAG_NO_FREE_DATA | BSON_FLAG_NO_FREE_OBJECT); if ((bson->flags & BSON_FLAG_CHILD)) { - achild->depth = ((bson_impl_alloc_t *) bson)->depth + 1; + achild->depth = ((bson_impl_alloc_t *)bson)->depth + 1; } else { achild->depth = 1; } achild->parent = bson; - achild->buf = aparent->buf; - achild->buflen = aparent->buflen; + achild->indirect_buffer = aparent->indirect_buffer ? aparent->indirect_buffer : &aparent->own_buffer; + achild->indirect_buflen = aparent->indirect_buflen ? aparent->indirect_buflen : &aparent->own_buflen; achild->offset = aparent->offset + aparent->len - 1 - 5; achild->len = 5; - achild->alloc = NULL; - achild->alloclen = 0; + achild->own_buffer = NULL; + achild->own_buflen = 0; achild->realloc = aparent->realloc; achild->realloc_func_ctx = aparent->realloc_func_ctx; @@ -545,12 +487,12 @@ _bson_append_bson_begin (bson_t *bson, /* IN */ */ static bool -_bson_append_bson_end (bson_t *bson, /* IN */ - bson_t *child) /* IN */ +_bson_append_bson_end(bson_t *bson, /* IN */ + bson_t *child) /* IN */ { - BSON_ASSERT (bson); - BSON_ASSERT ((bson->flags & BSON_FLAG_IN_CHILD)); - BSON_ASSERT (!(child->flags & BSON_FLAG_IN_CHILD)); + BSON_ASSERT(bson); + BSON_ASSERT((bson->flags & BSON_FLAG_IN_CHILD)); + BSON_ASSERT(!(child->flags & BSON_FLAG_IN_CHILD)); /* * Unmark the IN_CHILD flag. @@ -567,49 +509,32 @@ _bson_append_bson_end (bson_t *bson, /* IN */ * Ensure we have a \0 byte at the end and proper length encoded at * the beginning of the document. */ - _bson_data (bson)[bson->len - 1] = '\0'; - _bson_encode_length (bson); + _bson_data(bson)[bson->len - 1] = '\0'; + _bson_encode_length(bson); return true; } - -/* - *-------------------------------------------------------------------------- - * - * bson_append_array_begin -- - * - * Start appending a new array. - * - * Use @child to append to the data area for the given field. - * - * It is a programming error to call any other bson function on - * @bson until bson_append_array_end() has been called. It is - * valid to call bson_append*() functions on @child. - * - * This function is useful to allow building nested documents using - * a single buffer owned by the top-level bson document. - * - * Returns: - * true if successful; otherwise false and @child is invalid. - * - * Side effects: - * @child is initialized if true is returned. - * - *-------------------------------------------------------------------------- - */ +bool +bson_append_array_begin(bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + bson_t *child) /* IN */ +{ + return bson_append_array_unsafe_begin(bson, key, key_length, child); +} bool -bson_append_array_begin (bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - bson_t *child) /* IN */ +bson_append_array_unsafe_begin(bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + bson_t *child) /* IN */ { - BSON_ASSERT (bson); - BSON_ASSERT (key); - BSON_ASSERT (child); + BSON_ASSERT(bson); + BSON_ASSERT(key); + BSON_ASSERT(child); - return _bson_append_bson_begin (bson, key, key_length, BSON_TYPE_ARRAY, child); + return _bson_append_bson_begin(bson, key, key_length, BSON_TYPE_ARRAY, child); } @@ -633,13 +558,13 @@ bson_append_array_begin (bson_t *bson, /* IN */ */ bool -bson_append_array_end (bson_t *bson, /* IN */ - bson_t *child) /* IN */ +bson_append_array_end(bson_t *bson, /* IN */ + bson_t *child) /* IN */ { - BSON_ASSERT (bson); - BSON_ASSERT (child); + BSON_ASSERT(bson); + BSON_ASSERT(child); - return _bson_append_bson_end (bson, child); + return _bson_append_bson_end(bson, child); } @@ -668,16 +593,16 @@ bson_append_array_end (bson_t *bson, /* IN */ *-------------------------------------------------------------------------- */ bool -bson_append_document_begin (bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - bson_t *child) /* IN */ +bson_append_document_begin(bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + bson_t *child) /* IN */ { - BSON_ASSERT (bson); - BSON_ASSERT (key); - BSON_ASSERT (child); + BSON_ASSERT(bson); + BSON_ASSERT(key); + BSON_ASSERT(child); - return _bson_append_bson_begin (bson, key, key_length, BSON_TYPE_DOCUMENT, child); + return _bson_append_bson_begin(bson, key, key_length, BSON_TYPE_DOCUMENT, child); } @@ -701,13 +626,13 @@ bson_append_document_begin (bson_t *bson, /* IN */ */ bool -bson_append_document_end (bson_t *bson, /* IN */ - bson_t *child) /* IN */ +bson_append_document_end(bson_t *bson, /* IN */ + bson_t *child) /* IN */ { - BSON_ASSERT (bson); - BSON_ASSERT (child); + BSON_ASSERT(bson); + BSON_ASSERT(child); - return _bson_append_bson_end (bson, child); + return _bson_append_bson_end(bson, child); } @@ -731,55 +656,64 @@ bson_append_document_end (bson_t *bson, /* IN */ */ bool -bson_append_array (bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - const bson_t *array) /* IN */ +bson_append_array(bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + const bson_t *array) /* IN */ { static const uint8_t type = BSON_TYPE_ARRAY; - BSON_ASSERT (bson); - BSON_ASSERT (key); - BSON_ASSERT (array); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + BSON_ASSERT_PARAM(array); + + BSON_APPEND_BYTES_LIST_DECLARE(args); - HANDLE_KEY_LENGTH (key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); /* * Let's be a bit pedantic and ensure the array has properly formatted key * names. We will verify this simply by checking the first element for "0" * if the array is non-empty. */ - if (array && !bson_empty (array)) { + if (array && !bson_empty(array)) { bson_iter_t iter; - if (bson_iter_init (&iter, array) && bson_iter_next (&iter)) { - if (0 != strcmp ("0", bson_iter_key (&iter))) { - fprintf (stderr, - "%s(): invalid array detected. first element of array " - "parameter is not \"0\".\n", - BSON_FUNC); + if (bson_iter_init(&iter, array) && bson_iter_next(&iter)) { + if (0 != strcmp("0", bson_iter_key(&iter))) { + fprintf(stderr, + "%s(): invalid array detected. first element of array " + "parameter is not \"0\".\n", + BSON_FUNC); } } } - return _bson_append ( - bson, 4, (1 + key_length + 1 + array->len), 1, &type, key_length, key, 1, &gZero, array->len, _bson_data (array)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, _bson_data(array), array->len); + + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); + + return true; + +append_failure: + return false; } /* *-------------------------------------------------------------------------- * - * bson_append_binary -- + * _bson_append_binary -- * - * Append binary data to @bson. The field will have the - * BSON_TYPE_BINARY type. + * Append a BSON_TYPE_BINARY field, optionally copying @binary into the field. * * Parameters: * @subtype: the BSON Binary Subtype. See bsonspec.org for more * information. - * @binary: a pointer to the raw binary data. - * @length: the size of @binary in bytes. + * @binary: Optional pointer to the raw binary data. + * @length: the size of the field's binary data in bytes. * * Returns: * true if successful; otherwise false. @@ -790,8 +724,8 @@ bson_append_array (bson_t *bson, /* IN */ *-------------------------------------------------------------------------- */ -bool -bson_append_binary (bson_t *bson, /* IN */ +static bool +_bson_append_binary(bson_t *bson, /* IN */ const char *key, /* IN */ int key_length, /* IN */ bson_subtype_t subtype, /* IN */ @@ -799,67 +733,63 @@ bson_append_binary (bson_t *bson, /* IN */ uint32_t length) /* IN */ { static const uint8_t type = BSON_TYPE_BINARY; - uint32_t length_le; - uint32_t deprecated_length_le; - uint8_t subtype8 = 0; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + BSON_OPTIONAL_PARAM(binary); - HANDLE_KEY_LENGTH (key, key_length); + BSON_APPEND_BYTES_LIST_DECLARE(args); - subtype8 = subtype; + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + + const uint8_t subtype_arg = (uint8_t)subtype; if (subtype == BSON_SUBTYPE_BINARY_DEPRECATED) { - length_le = BSON_UINT32_TO_LE (length + 4); - deprecated_length_le = BSON_UINT32_TO_LE (length); - - return _bson_append (bson, - 7, - (1 + key_length + 1 + 4 + 1 + 4 + length), - 1, - &type, - key_length, - key, - 1, - &gZero, - 4, - &length_le, - 1, - &subtype8, - 4, - &deprecated_length_le, - length, - binary); + if (length > UINT32_MAX - 4u) { + return false; + } + + const uint32_t length_le = BSON_UINT32_TO_LE(length + 4u); + const uint32_t length_arg = BSON_UINT32_TO_LE(length); + + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &length_le, sizeof(length_le)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &subtype_arg, sizeof(subtype_arg)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &length_arg, sizeof(length_arg)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, binary, length); + + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); } else { - length_le = BSON_UINT32_TO_LE (length); + const uint32_t length_arg = BSON_UINT32_TO_LE(length); - return _bson_append (bson, - 6, - (1 + key_length + 1 + 4 + 1 + length), - 1, - &type, - key_length, - key, - 1, - &gZero, - 4, - &length_le, - 1, - &subtype8, - length, - binary); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &length_arg, sizeof(length_arg)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &subtype_arg, sizeof(subtype_arg)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, binary, length); + + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); } + + return true; + +append_failure: + return false; } /* *-------------------------------------------------------------------------- * - * bson_append_bool -- + * bson_append_binary -- * - * Append a new field to @bson with the name @key. The value is - * a boolean indicated by @value. + * Append binary data to @bson. The field will have the + * BSON_TYPE_BINARY type. + * + * Parameters: + * @subtype: the BSON Binary Subtype. See bsonspec.org for more + * information. + * @binary: a pointer to the raw binary data. + * @length: the size of @binary in bytes. * * Returns: * true if successful; otherwise false. @@ -871,32 +801,35 @@ bson_append_binary (bson_t *bson, /* IN */ */ bool -bson_append_bool (bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - bool value) /* IN */ +bson_append_binary(bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + bson_subtype_t subtype, /* IN */ + const uint8_t *binary, /* IN */ + uint32_t length) /* IN */ { - static const uint8_t type = BSON_TYPE_BOOL; - uint8_t abyte = !!value; - - BSON_ASSERT (bson); - BSON_ASSERT (key); - - HANDLE_KEY_LENGTH (key, key_length); - - return _bson_append (bson, 4, (1 + key_length + 1 + 1), 1, &type, key_length, key, 1, &gZero, 1, &abyte); + if (!binary && length > 0u) { + return false; + } + return _bson_append_binary(bson, key, key_length, subtype, binary, length); } /* *-------------------------------------------------------------------------- * - * bson_append_code -- + * bson_append_binary_uninit -- * - * Append a new field to @bson containing javascript code. + * Append binary data to @bson by framing an uninitialized field to be written by the caller. + * The field will have the BSON_TYPE_BINARY type. On success, the caller MUST write to all + * bytes in the binary data field. The returned `*binary` pointer may be invalidated by + * subsequent modifications to @bson. * - * @javascript MUST be a zero terminated UTF-8 string. It MUST NOT - * containing embedded \0 characters. + * Parameters: + * @subtype: the BSON Binary Subtype. See bsonspec.org for more + * information. + * @binary: Output parameter for a temporary pointer where the binary item's contents must be written. + * @length: the size of @binary in bytes. * * Returns: * true if successful; otherwise false. @@ -904,54 +837,34 @@ bson_append_bool (bson_t *bson, /* IN */ * Side effects: * None. * - * See also: - * bson_append_code_with_scope(). - * *-------------------------------------------------------------------------- */ bool -bson_append_code (bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - const char *javascript) /* IN */ +bson_append_binary_uninit(bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + bson_subtype_t subtype, /* IN */ + uint8_t **binary, /* IN */ + uint32_t length) /* IN */ { - static const uint8_t type = BSON_TYPE_CODE; - uint32_t length; - uint32_t length_le; - - BSON_ASSERT (bson); - BSON_ASSERT (key); - BSON_ASSERT (javascript); - - HANDLE_KEY_LENGTH (key, key_length); - - length = (int) strlen (javascript) + 1; - length_le = BSON_UINT32_TO_LE (length); - - return _bson_append (bson, - 5, - (1 + key_length + 1 + 4 + length), - 1, - &type, - key_length, - key, - 1, - &gZero, - 4, - &length_le, - length, - javascript); + BSON_ASSERT_PARAM(binary); + if (_bson_append_binary(bson, key, key_length, subtype, NULL, length)) { + *binary = _bson_data(bson) + bson->len - 1u - length; + return true; + } else { + return false; + } } /* *-------------------------------------------------------------------------- * - * bson_append_code_with_scope -- + * bson_append_bool -- * - * Append a new field to @bson containing javascript code with - * supplied scope. + * Append a new field to @bson with the name @key. The value is + * a boolean indicated by @value. * * Returns: * true if successful; otherwise false. @@ -963,62 +876,44 @@ bson_append_code (bson_t *bson, /* IN */ */ bool -bson_append_code_with_scope (bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - const char *javascript, /* IN */ - const bson_t *scope) /* IN */ +bson_append_bool(bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + bool value) /* IN */ { - static const uint8_t type = BSON_TYPE_CODEWSCOPE; - uint32_t codews_length_le; - uint32_t codews_length; - uint32_t js_length_le; - uint32_t js_length; + static const uint8_t type = BSON_TYPE_BOOL; - BSON_ASSERT (bson); - BSON_ASSERT (key); - BSON_ASSERT (javascript); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); - if (scope == NULL) { - return bson_append_code (bson, key, key_length, javascript); - } + BSON_APPEND_BYTES_LIST_DECLARE(args); + + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - HANDLE_KEY_LENGTH (key, key_length); + const uint8_t byte_arg = value ? 1u : 0u; - js_length = (int) strlen (javascript) + 1; - js_length_le = BSON_UINT32_TO_LE (js_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &byte_arg, sizeof(byte_arg)); - codews_length = 4 + 4 + js_length + scope->len; - codews_length_le = BSON_UINT32_TO_LE (codews_length); + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - return _bson_append (bson, - 7, - (1 + key_length + 1 + 4 + 4 + js_length + scope->len), - 1, - &type, - key_length, - key, - 1, - &gZero, - 4, - &codews_length_le, - 4, - &js_length_le, - js_length, - javascript, - scope->len, - _bson_data (scope)); + return true; + +append_failure: + return false; } /* *-------------------------------------------------------------------------- * - * bson_append_dbpointer -- + * bson_append_code -- * - * This BSON data type is DEPRECATED. + * Append a new field to @bson containing javascript code. * - * Append a BSON dbpointer field to @bson. + * @javascript MUST be a zero terminated UTF-8 string. It MUST NOT + * containing embedded \0 characters. * * Returns: * true if successful; otherwise false. @@ -1026,52 +921,182 @@ bson_append_code_with_scope (bson_t *bson, /* IN */ * Side effects: * None. * + * See also: + * bson_append_code_with_scope(). + * *-------------------------------------------------------------------------- */ bool -bson_append_dbpointer (bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - const char *collection, /* IN */ - const bson_oid_t *oid) +bson_append_code(bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + const char *javascript) /* IN */ { - static const uint8_t type = BSON_TYPE_DBPOINTER; - uint32_t length; - uint32_t length_le; + static const uint8_t type = BSON_TYPE_CODE; - BSON_ASSERT (bson); - BSON_ASSERT (key); - BSON_ASSERT (collection); - BSON_ASSERT (oid); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + BSON_ASSERT_PARAM(javascript); - HANDLE_KEY_LENGTH (key, key_length); + const size_t zulength = strlen(javascript); + if (zulength > UINT32_MAX - 1u) { + return false; + } + const uint32_t length = (uint32_t)zulength + 1u; + const uint32_t length_arg = BSON_UINT32_TO_LE(length); + + BSON_APPEND_BYTES_LIST_DECLARE(args); + + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &length_arg, sizeof(length_arg)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, javascript, length); - length = (int) strlen (collection) + 1; - length_le = BSON_UINT32_TO_LE (length); + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - return _bson_append (bson, - 6, - (1 + key_length + 1 + 4 + length + 12), - 1, - &type, - key_length, - key, - 1, - &gZero, - 4, - &length_le, - length, - collection, - 12, - oid); + return true; + +append_failure: + return false; } /* *-------------------------------------------------------------------------- * - * bson_append_document -- + * bson_append_code_with_scope -- + * + * Append a new field to @bson containing javascript code with + * supplied scope. + * + * Returns: + * true if successful; otherwise false. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +bool +bson_append_code_with_scope(bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + const char *javascript, /* IN */ + const bson_t *scope) /* IN */ +{ + static const uint8_t type = BSON_TYPE_CODEWSCOPE; + + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + BSON_ASSERT_PARAM(javascript); + + if (!scope) { + return bson_append_code(bson, key, key_length, javascript); + } + + const size_t zulength = strlen(javascript); + if (zulength > UINT32_MAX - 1u) { + return false; + } + + const uint32_t js_length = (uint32_t)zulength + 1u; + const uint32_t js_length_arg = BSON_UINT32_TO_LE(js_length); + + if (js_length > UINT32_MAX - scope->len) { + return false; + } + + if (js_length + scope->len > UINT32_MAX - (2u * sizeof(uint32_t))) { + return false; + } + + const uint32_t total_length = (uint32_t)(2u * sizeof(uint32_t)) + js_length + scope->len; + const uint32_t total_length_arg = BSON_UINT32_TO_LE(total_length); + + BSON_APPEND_BYTES_LIST_DECLARE(args); + + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &total_length_arg, sizeof(total_length_arg)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &js_length_arg, sizeof(js_length_arg)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, javascript, js_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, _bson_data(scope), scope->len); + + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); + + return true; + +append_failure: + return false; +} + + +/* + *-------------------------------------------------------------------------- + * + * bson_append_dbpointer -- + * + * This BSON data type is DEPRECATED. + * + * Append a BSON dbpointer field to @bson. + * + * Returns: + * true if successful; otherwise false. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +bool +bson_append_dbpointer(bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + const char *collection, /* IN */ + const bson_oid_t *oid) +{ + static const uint8_t type = BSON_TYPE_DBPOINTER; + + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + BSON_ASSERT_PARAM(collection); + BSON_ASSERT_PARAM(oid); + + const size_t zulength = strlen(collection); + if (zulength > UINT32_MAX - 1u) { + return false; + } + + const uint32_t length = (uint32_t)zulength + 1u; + const uint32_t length_arg = BSON_UINT32_TO_LE(length); + + BSON_APPEND_BYTES_LIST_DECLARE(args); + + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &length_arg, sizeof(length_arg)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, collection, length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, oid->bytes, sizeof(oid->bytes)); + + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); + + return true; + +append_failure: + return false; +} + + +/* + *-------------------------------------------------------------------------- + * + * bson_append_document -- * * Append a new field to @bson containing a BSON document. * @@ -1091,131 +1116,176 @@ bson_append_dbpointer (bson_t *bson, /* IN */ */ bool -bson_append_document (bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - const bson_t *value) /* IN */ +bson_append_document(bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + const bson_t *value) /* IN */ { static const uint8_t type = BSON_TYPE_DOCUMENT; - BSON_ASSERT (bson); - BSON_ASSERT (key); - BSON_ASSERT (value); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + BSON_ASSERT_PARAM(value); + + BSON_APPEND_BYTES_LIST_DECLARE(args); + + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, _bson_data(value), value->len); - HANDLE_KEY_LENGTH (key, key_length); + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - return _bson_append ( - bson, 4, (1 + key_length + 1 + value->len), 1, &type, key_length, key, 1, &gZero, value->len, _bson_data (value)); + return true; + +append_failure: + return false; } bool -bson_append_double (bson_t *bson, const char *key, int key_length, double value) +bson_append_double(bson_t *bson, const char *key, int key_length, double value) { static const uint8_t type = BSON_TYPE_DOUBLE; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); - HANDLE_KEY_LENGTH (key, key_length); + const double value_arg = BSON_DOUBLE_TO_LE(value); -#if BSON_BYTE_ORDER == BSON_BIG_ENDIAN - value = BSON_DOUBLE_TO_LE (value); -#endif + BSON_APPEND_BYTES_LIST_DECLARE(args); - return _bson_append (bson, 4, (1 + key_length + 1 + 8), 1, &type, key_length, key, 1, &gZero, 8, &value); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &value_arg, sizeof(value_arg)); + + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); + + return true; + +append_failure: + return false; } bool -bson_append_int32 (bson_t *bson, const char *key, int key_length, int32_t value) +bson_append_int32(bson_t *bson, const char *key, int key_length, int32_t value) { static const uint8_t type = BSON_TYPE_INT32; - uint32_t value_le; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + + const uint32_t value_arg = BSON_UINT32_TO_LE(value); + + BSON_APPEND_BYTES_LIST_DECLARE(args); + + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &value_arg, sizeof(value_arg)); - HANDLE_KEY_LENGTH (key, key_length); + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - value_le = BSON_UINT32_TO_LE (value); + return true; - return _bson_append (bson, 4, (1 + key_length + 1 + 4), 1, &type, key_length, key, 1, &gZero, 4, &value_le); +append_failure: + return false; } bool -bson_append_int64 (bson_t *bson, const char *key, int key_length, int64_t value) +bson_append_int64(bson_t *bson, const char *key, int key_length, int64_t value) { static const uint8_t type = BSON_TYPE_INT64; - uint64_t value_le; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + + const uint64_t value_arg = BSON_UINT64_TO_LE(value); + + BSON_APPEND_BYTES_LIST_DECLARE(args); - HANDLE_KEY_LENGTH (key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &value_arg, sizeof(value_arg)); - value_le = BSON_UINT64_TO_LE (value); + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - return _bson_append (bson, 4, (1 + key_length + 1 + 8), 1, &type, key_length, key, 1, &gZero, 8, &value_le); + return true; + +append_failure: + return false; } bool -bson_append_decimal128 (bson_t *bson, const char *key, int key_length, const bson_decimal128_t *value) +bson_append_decimal128(bson_t *bson, const char *key, int key_length, const bson_decimal128_t *value) { static const uint8_t type = BSON_TYPE_DECIMAL128; - uint64_t value_le[2]; - BSON_ASSERT (bson); - BSON_ASSERT (key); - BSON_ASSERT (value); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + + const uint64_t value_arg[] = { + BSON_UINT64_TO_LE(value->low), + BSON_UINT64_TO_LE(value->high), + }; - HANDLE_KEY_LENGTH (key, key_length); + BSON_APPEND_BYTES_LIST_DECLARE(args); - value_le[0] = BSON_UINT64_TO_LE (value->low); - value_le[1] = BSON_UINT64_TO_LE (value->high); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &value_arg, sizeof(value_arg)); - return _bson_append (bson, 4, (1 + key_length + 1 + 16), 1, &type, key_length, key, 1, &gZero, 16, value_le); + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); + + return true; + +append_failure: + return false; } bool -bson_append_iter (bson_t *bson, const char *key, int key_length, const bson_iter_t *iter) +bson_append_iter(bson_t *bson, const char *key, int key_length, const bson_iter_t *iter) { bool ret = false; - BSON_ASSERT (bson); - BSON_ASSERT (iter); + BSON_ASSERT(bson); + BSON_ASSERT(iter); if (!key) { - key = bson_iter_key (iter); + key = bson_iter_key(iter); key_length = -1; } - switch (bson_iter_type_unsafe (iter)) { + switch (bson_iter_type_unsafe(iter)) { case BSON_TYPE_EOD: return false; case BSON_TYPE_DOUBLE: - ret = bson_append_double (bson, key, key_length, bson_iter_double (iter)); + ret = bson_append_double(bson, key, key_length, bson_iter_double(iter)); break; case BSON_TYPE_UTF8: { uint32_t len = 0; const char *str; - str = bson_iter_utf8 (iter, &len); - ret = bson_append_utf8 (bson, key, key_length, str, len); + str = bson_iter_utf8(iter, &len); + ret = bson_append_utf8(bson, key, key_length, str, len); } break; case BSON_TYPE_DOCUMENT: { const uint8_t *buf = NULL; uint32_t len = 0; bson_t doc; - bson_iter_document (iter, &len, &buf); + bson_iter_document(iter, &len, &buf); - if (bson_init_static (&doc, buf, len)) { - ret = bson_append_document (bson, key, key_length, &doc); - bson_destroy (&doc); + if (bson_init_static(&doc, buf, len)) { + ret = bson_append_document(bson, key, key_length, &doc); + bson_destroy(&doc); } } break; case BSON_TYPE_ARRAY: { @@ -1223,11 +1293,11 @@ bson_append_iter (bson_t *bson, const char *key, int key_length, const bson_iter uint32_t len = 0; bson_t doc; - bson_iter_array (iter, &len, &buf); + bson_iter_array(iter, &len, &buf); - if (bson_init_static (&doc, buf, len)) { - ret = bson_append_array (bson, key, key_length, &doc); - bson_destroy (&doc); + if (bson_init_static(&doc, buf, len)) { + ret = bson_append_array(bson, key, key_length, &doc); + bson_destroy(&doc); } } break; case BSON_TYPE_BINARY: { @@ -1235,52 +1305,52 @@ bson_append_iter (bson_t *bson, const char *key, int key_length, const bson_iter bson_subtype_t subtype = BSON_SUBTYPE_BINARY; uint32_t len = 0; - bson_iter_binary (iter, &subtype, &len, &binary); - ret = bson_append_binary (bson, key, key_length, subtype, binary, len); + bson_iter_binary(iter, &subtype, &len, &binary); + ret = bson_append_binary(bson, key, key_length, subtype, binary, len); } break; case BSON_TYPE_UNDEFINED: - ret = bson_append_undefined (bson, key, key_length); + ret = bson_append_undefined(bson, key, key_length); break; case BSON_TYPE_OID: - ret = bson_append_oid (bson, key, key_length, bson_iter_oid (iter)); + ret = bson_append_oid(bson, key, key_length, bson_iter_oid(iter)); break; case BSON_TYPE_BOOL: - ret = bson_append_bool (bson, key, key_length, bson_iter_bool (iter)); + ret = bson_append_bool(bson, key, key_length, bson_iter_bool(iter)); break; case BSON_TYPE_DATE_TIME: - ret = bson_append_date_time (bson, key, key_length, bson_iter_date_time (iter)); + ret = bson_append_date_time(bson, key, key_length, bson_iter_date_time(iter)); break; case BSON_TYPE_NULL: - ret = bson_append_null (bson, key, key_length); + ret = bson_append_null(bson, key, key_length); break; case BSON_TYPE_REGEX: { const char *regex; const char *options; - regex = bson_iter_regex (iter, &options); - ret = bson_append_regex (bson, key, key_length, regex, options); + regex = bson_iter_regex(iter, &options); + ret = bson_append_regex(bson, key, key_length, regex, options); } break; case BSON_TYPE_DBPOINTER: { const bson_oid_t *oid; uint32_t len; const char *collection; - bson_iter_dbpointer (iter, &len, &collection, &oid); - ret = bson_append_dbpointer (bson, key, key_length, collection, oid); + bson_iter_dbpointer(iter, &len, &collection, &oid); + ret = bson_append_dbpointer(bson, key, key_length, collection, oid); } break; case BSON_TYPE_CODE: { uint32_t len; const char *code; - code = bson_iter_code (iter, &len); - ret = bson_append_code (bson, key, key_length, code); + code = bson_iter_code(iter, &len); + ret = bson_append_code(bson, key, key_length, code); } break; case BSON_TYPE_SYMBOL: { uint32_t len; const char *symbol; - symbol = bson_iter_symbol (iter, &len); - ret = bson_append_symbol (bson, key, key_length, symbol, len); + symbol = bson_iter_symbol(iter, &len); + ret = bson_append_symbol(bson, key, key_length, symbol, len); } break; case BSON_TYPE_CODEWSCOPE: { const uint8_t *scope = NULL; @@ -1289,40 +1359,40 @@ bson_append_iter (bson_t *bson, const char *key, int key_length, const bson_iter const char *javascript = NULL; bson_t doc; - javascript = bson_iter_codewscope (iter, &len, &scope_len, &scope); + javascript = bson_iter_codewscope(iter, &len, &scope_len, &scope); - if (bson_init_static (&doc, scope, scope_len)) { - ret = bson_append_code_with_scope (bson, key, key_length, javascript, &doc); - bson_destroy (&doc); + if (bson_init_static(&doc, scope, scope_len)) { + ret = bson_append_code_with_scope(bson, key, key_length, javascript, &doc); + bson_destroy(&doc); } } break; case BSON_TYPE_INT32: - ret = bson_append_int32 (bson, key, key_length, bson_iter_int32 (iter)); + ret = bson_append_int32(bson, key, key_length, bson_iter_int32(iter)); break; case BSON_TYPE_TIMESTAMP: { uint32_t ts; uint32_t inc; - bson_iter_timestamp (iter, &ts, &inc); - ret = bson_append_timestamp (bson, key, key_length, ts, inc); + bson_iter_timestamp(iter, &ts, &inc); + ret = bson_append_timestamp(bson, key, key_length, ts, inc); } break; case BSON_TYPE_INT64: - ret = bson_append_int64 (bson, key, key_length, bson_iter_int64 (iter)); + ret = bson_append_int64(bson, key, key_length, bson_iter_int64(iter)); break; case BSON_TYPE_DECIMAL128: { bson_decimal128_t dec; - if (!bson_iter_decimal128 (iter, &dec)) { + if (!bson_iter_decimal128(iter, &dec)) { return false; } - ret = bson_append_decimal128 (bson, key, key_length, &dec); + ret = bson_append_decimal128(bson, key, key_length, &dec); } break; case BSON_TYPE_MAXKEY: - ret = bson_append_maxkey (bson, key, key_length); + ret = bson_append_maxkey(bson, key, key_length); break; case BSON_TYPE_MINKEY: - ret = bson_append_minkey (bson, key, key_length); + ret = bson_append_minkey(bson, key, key_length); break; default: break; @@ -1333,125 +1403,116 @@ bson_append_iter (bson_t *bson, const char *key, int key_length, const bson_iter bool -bson_append_maxkey (bson_t *bson, const char *key, int key_length) +bson_append_maxkey(bson_t *bson, const char *key, int key_length) { static const uint8_t type = BSON_TYPE_MAXKEY; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); - HANDLE_KEY_LENGTH (key, key_length); + BSON_APPEND_BYTES_LIST_DECLARE(args); + + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); + + return true; - return _bson_append (bson, 3, (1 + key_length + 1), 1, &type, key_length, key, 1, &gZero); +append_failure: + return false; } bool -bson_append_minkey (bson_t *bson, const char *key, int key_length) +bson_append_minkey(bson_t *bson, const char *key, int key_length) { static const uint8_t type = BSON_TYPE_MINKEY; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + + BSON_APPEND_BYTES_LIST_DECLARE(args); + + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - HANDLE_KEY_LENGTH (key, key_length); + return true; - return _bson_append (bson, 3, (1 + key_length + 1), 1, &type, key_length, key, 1, &gZero); +append_failure: + return false; } bool -bson_append_null (bson_t *bson, const char *key, int key_length) +bson_append_null(bson_t *bson, const char *key, int key_length) { static const uint8_t type = BSON_TYPE_NULL; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + + BSON_APPEND_BYTES_LIST_DECLARE(args); + + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - HANDLE_KEY_LENGTH (key, key_length); + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - return _bson_append (bson, 3, (1 + key_length + 1), 1, &type, key_length, key, 1, &gZero); + return true; + +append_failure: + return false; } bool -bson_append_oid (bson_t *bson, const char *key, int key_length, const bson_oid_t *value) +bson_append_oid(bson_t *bson, const char *key, int key_length, const bson_oid_t *value) { static const uint8_t type = BSON_TYPE_OID; - BSON_ASSERT (bson); - BSON_ASSERT (key); - BSON_ASSERT (value); - - HANDLE_KEY_LENGTH (key, key_length); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + BSON_ASSERT_PARAM(value); - return _bson_append (bson, 4, (1 + key_length + 1 + 12), 1, &type, key_length, key, 1, &gZero, 12, value); -} + BSON_APPEND_BYTES_LIST_DECLARE(args); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, value->bytes, sizeof(value->bytes)); -/* - *-------------------------------------------------------------------------- - * - * _bson_append_regex_options_sorted -- - * - * Helper to append regex options to a buffer in a sorted order. - * Any duplicate or unsupported options will be ignored. - * - * Parameters: - * @buffer: Buffer to which sorted options will be appended - * @options: Regex options - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); -static BSON_INLINE void -_bson_append_regex_options_sorted (bson_string_t *buffer, /* IN */ - const char *options) /* IN */ -{ - const char *c; + return true; - for (c = BSON_REGEX_OPTIONS_SORTED; *c; c++) { - if (strchr (options, *c)) { - bson_string_append_c (buffer, *c); - } - } +append_failure: + return false; } bool -bson_append_regex (bson_t *bson, const char *key, int key_length, const char *regex, const char *options) +bson_append_regex(bson_t *bson, const char *key, int key_length, const char *regex, const char *options) { - return bson_append_regex_w_len (bson, key, key_length, regex, -1, options); + return bson_append_regex_w_len(bson, key, key_length, regex, -1, options); } bool -bson_append_regex_w_len ( +bson_append_regex_w_len( bson_t *bson, const char *key, int key_length, const char *regex, int regex_length, const char *options) { static const uint8_t type = BSON_TYPE_REGEX; - bson_string_t *options_sorted; - bool r; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); - HANDLE_KEY_LENGTH (key, key_length); - - if (regex_length < 0) { - regex_length = (int) strlen (regex); - } else { - /* Necessary to validate embedded NULL is not present in key. */ - if (_string_contains_null (regex, regex_length)) { - return false; - } - } + bool ret = false; if (!regex) { regex = ""; @@ -1461,289 +1522,327 @@ bson_append_regex_w_len ( options = ""; } - options_sorted = bson_string_new (NULL); + size_t options_len = strlen(options); + mcommon_string_append_t options_sorted; + mcommon_string_new_with_capacity_as_append(&options_sorted, (uint32_t)options_len); + if (!mcommon_string_append_selected_chars(&options_sorted, BSON_REGEX_OPTIONS_SORTED, options, options_len)) { + goto append_failure; + } + + BSON_APPEND_BYTES_LIST_DECLARE(args); - _bson_append_regex_options_sorted (options_sorted, options); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, regex, regex_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT( + args, mcommon_str_from_append(&options_sorted), 1u + mcommon_strlen_from_append(&options_sorted)); - r = _bson_append (bson, - 6, - (1 + key_length + 1 + regex_length + 1 + options_sorted->len + 1), - 1, - &type, - key_length, - key, - 1, - &gZero, - regex_length, - regex, - 1, - &gZero, - options_sorted->len + 1, - options_sorted->str); + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - bson_string_free (options_sorted, true); + ret = true; - return r; +append_failure: + mcommon_string_from_append_destroy(&options_sorted); + + return ret; } bool -bson_append_utf8 (bson_t *bson, const char *key, int key_length, const char *value, int length) +bson_append_utf8(bson_t *bson, const char *key, int key_length, const char *value, int length) { static const uint8_t type = BSON_TYPE_UTF8; - uint32_t length_le; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); - if (BSON_UNLIKELY (!value)) { - return bson_append_null (bson, key, key_length); + if (BSON_UNLIKELY(!value)) { + return bson_append_null(bson, key, key_length); } - HANDLE_KEY_LENGTH (key, key_length); + size_t zulength; + if (BSON_UNLIKELY(length < 0)) { + zulength = strlen(value); + } else { + zulength = (size_t)length; + } - if (BSON_UNLIKELY (length < 0)) { - length = (int) strlen (value); + if (zulength > UINT32_MAX - 1u) { + return false; } - length_le = BSON_UINT32_TO_LE (length + 1); + const uint32_t ulength = (uint32_t)zulength; + const uint32_t ulength_arg = BSON_UINT32_TO_LE(ulength + 1u); + + BSON_APPEND_BYTES_LIST_DECLARE(args); - return _bson_append (bson, - 6, - (1 + key_length + 1 + 4 + length + 1), - 1, - &type, - key_length, - key, - 1, - &gZero, - 4, - &length_le, - length, - value, - 1, - &gZero); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &ulength_arg, sizeof(ulength_arg)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, value, ulength); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); + + return true; + +append_failure: + return false; } bool -bson_append_symbol (bson_t *bson, const char *key, int key_length, const char *value, int length) +bson_append_symbol(bson_t *bson, const char *key, int key_length, const char *value, int length) { static const uint8_t type = BSON_TYPE_SYMBOL; - uint32_t length_le; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); if (!value) { - return bson_append_null (bson, key, key_length); + return bson_append_null(bson, key, key_length); } - HANDLE_KEY_LENGTH (key, key_length); + size_t zulength; + if (BSON_UNLIKELY(length < 0)) { + zulength = strlen(value); + } else { + zulength = (size_t)length; + } - if (length < 0) { - length = (int) strlen (value); + if (zulength > UINT32_MAX - 1u) { + return false; } - length_le = BSON_UINT32_TO_LE (length + 1); + const uint32_t ulength = (uint32_t)zulength; + const uint32_t ulength_arg = BSON_UINT32_TO_LE(ulength + 1u); - return _bson_append (bson, - 6, - (1 + key_length + 1 + 4 + length + 1), - 1, - &type, - key_length, - key, - 1, - &gZero, - 4, - &length_le, - length, - value, - 1, - &gZero); + BSON_APPEND_BYTES_LIST_DECLARE(args); + + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &ulength_arg, sizeof(ulength_arg)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, value, ulength); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); + + return true; + +append_failure: + return false; } bool -bson_append_time_t (bson_t *bson, const char *key, int key_length, time_t value) +bson_append_time_t(bson_t *bson, const char *key, int key_length, time_t value) { #ifdef BSON_OS_WIN32 - struct timeval tv = {(long) value, 0}; + struct timeval tv = {(long)value, 0}; #else struct timeval tv = {value, 0}; #endif - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT(bson); + BSON_ASSERT(key); - return bson_append_timeval (bson, key, key_length, &tv); + return bson_append_timeval(bson, key, key_length, &tv); } bool -bson_append_timestamp (bson_t *bson, const char *key, int key_length, uint32_t timestamp, uint32_t increment) +bson_append_timestamp(bson_t *bson, const char *key, int key_length, uint32_t timestamp, uint32_t increment) { static const uint8_t type = BSON_TYPE_TIMESTAMP; - uint64_t value; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + + const uint64_t value = BSON_UINT64_TO_LE(((((uint64_t)timestamp) << 32) | ((uint64_t)increment))); + + BSON_APPEND_BYTES_LIST_DECLARE(args); - HANDLE_KEY_LENGTH (key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &value, sizeof(value)); - value = ((((uint64_t) timestamp) << 32) | ((uint64_t) increment)); - value = BSON_UINT64_TO_LE (value); + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - return _bson_append (bson, 4, (1 + key_length + 1 + 8), 1, &type, key_length, key, 1, &gZero, 8, &value); + return true; + +append_failure: + return false; } bool -bson_append_now_utc (bson_t *bson, const char *key, int key_length) +bson_append_now_utc(bson_t *bson, const char *key, int key_length) { - BSON_ASSERT (bson); - BSON_ASSERT (key); - BSON_ASSERT (key_length >= -1); + BSON_ASSERT(bson); + BSON_ASSERT(key); + BSON_ASSERT(key_length >= -1); - return bson_append_time_t (bson, key, key_length, time (NULL)); + return bson_append_time_t(bson, key, key_length, time(NULL)); } bool -bson_append_date_time (bson_t *bson, const char *key, int key_length, int64_t value) +bson_append_date_time(bson_t *bson, const char *key, int key_length, int64_t value) { static const uint8_t type = BSON_TYPE_DATE_TIME; - uint64_t value_le; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + + const uint64_t value_arg = BSON_UINT64_TO_LE(value); - HANDLE_KEY_LENGTH (key, key_length); + BSON_APPEND_BYTES_LIST_DECLARE(args); - value_le = BSON_UINT64_TO_LE (value); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &value_arg, sizeof(value_arg)); - return _bson_append (bson, 4, (1 + key_length + 1 + 8), 1, &type, key_length, key, 1, &gZero, 8, &value_le); + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); + + return true; + +append_failure: + return false; } bool -bson_append_timeval (bson_t *bson, const char *key, int key_length, struct timeval *value) +bson_append_timeval(bson_t *bson, const char *key, int key_length, struct timeval *value) { uint64_t unix_msec; - BSON_ASSERT (bson); - BSON_ASSERT (key); - BSON_ASSERT (value); + BSON_ASSERT(bson); + BSON_ASSERT(key); + BSON_ASSERT(value); - unix_msec = (((uint64_t) value->tv_sec) * 1000UL) + (value->tv_usec / 1000UL); - return bson_append_date_time (bson, key, key_length, unix_msec); + unix_msec = (((uint64_t)value->tv_sec) * 1000UL) + (value->tv_usec / 1000UL); + return bson_append_date_time(bson, key, key_length, unix_msec); } bool -bson_append_undefined (bson_t *bson, const char *key, int key_length) +bson_append_undefined(bson_t *bson, const char *key, int key_length) { static const uint8_t type = BSON_TYPE_UNDEFINED; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); - HANDLE_KEY_LENGTH (key, key_length); + BSON_APPEND_BYTES_LIST_DECLARE(args); - return _bson_append (bson, 3, (1 + key_length + 1), 1, &type, key_length, key, 1, &gZero); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); + BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); + BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); + + BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); + + return true; + +append_failure: + return false; } bool -bson_append_value (bson_t *bson, const char *key, int key_length, const bson_value_t *value) +bson_append_value(bson_t *bson, const char *key, int key_length, const bson_value_t *value) { bson_t local; bool ret = false; - BSON_ASSERT (bson); - BSON_ASSERT (key); - BSON_ASSERT (value); + BSON_ASSERT(bson); + BSON_ASSERT(key); + BSON_ASSERT(value); switch (value->value_type) { case BSON_TYPE_DOUBLE: - ret = bson_append_double (bson, key, key_length, value->value.v_double); + ret = bson_append_double(bson, key, key_length, value->value.v_double); break; case BSON_TYPE_UTF8: - ret = bson_append_utf8 (bson, key, key_length, value->value.v_utf8.str, value->value.v_utf8.len); + ret = bson_append_utf8(bson, key, key_length, value->value.v_utf8.str, value->value.v_utf8.len); break; case BSON_TYPE_DOCUMENT: - if (bson_init_static (&local, value->value.v_doc.data, value->value.v_doc.data_len)) { - ret = bson_append_document (bson, key, key_length, &local); - bson_destroy (&local); + if (bson_init_static(&local, value->value.v_doc.data, value->value.v_doc.data_len)) { + ret = bson_append_document(bson, key, key_length, &local); + bson_destroy(&local); } break; case BSON_TYPE_ARRAY: - if (bson_init_static (&local, value->value.v_doc.data, value->value.v_doc.data_len)) { - ret = bson_append_array (bson, key, key_length, &local); - bson_destroy (&local); + if (bson_init_static(&local, value->value.v_doc.data, value->value.v_doc.data_len)) { + ret = bson_append_array(bson, key, key_length, &local); + bson_destroy(&local); } break; case BSON_TYPE_BINARY: - ret = bson_append_binary (bson, - key, - key_length, - value->value.v_binary.subtype, - value->value.v_binary.data, - value->value.v_binary.data_len); + ret = bson_append_binary(bson, + key, + key_length, + value->value.v_binary.subtype, + value->value.v_binary.data, + value->value.v_binary.data_len); break; case BSON_TYPE_UNDEFINED: - ret = bson_append_undefined (bson, key, key_length); + ret = bson_append_undefined(bson, key, key_length); break; case BSON_TYPE_OID: - ret = bson_append_oid (bson, key, key_length, &value->value.v_oid); + ret = bson_append_oid(bson, key, key_length, &value->value.v_oid); break; case BSON_TYPE_BOOL: - ret = bson_append_bool (bson, key, key_length, value->value.v_bool); + ret = bson_append_bool(bson, key, key_length, value->value.v_bool); break; case BSON_TYPE_DATE_TIME: - ret = bson_append_date_time (bson, key, key_length, value->value.v_datetime); + ret = bson_append_date_time(bson, key, key_length, value->value.v_datetime); break; case BSON_TYPE_NULL: - ret = bson_append_null (bson, key, key_length); + ret = bson_append_null(bson, key, key_length); break; case BSON_TYPE_REGEX: - ret = bson_append_regex (bson, key, key_length, value->value.v_regex.regex, value->value.v_regex.options); + ret = bson_append_regex(bson, key, key_length, value->value.v_regex.regex, value->value.v_regex.options); break; case BSON_TYPE_DBPOINTER: - ret = bson_append_dbpointer ( + ret = bson_append_dbpointer( bson, key, key_length, value->value.v_dbpointer.collection, &value->value.v_dbpointer.oid); break; case BSON_TYPE_CODE: - ret = bson_append_code (bson, key, key_length, value->value.v_code.code); + ret = bson_append_code(bson, key, key_length, value->value.v_code.code); break; case BSON_TYPE_SYMBOL: - ret = bson_append_symbol (bson, key, key_length, value->value.v_symbol.symbol, value->value.v_symbol.len); + ret = bson_append_symbol(bson, key, key_length, value->value.v_symbol.symbol, value->value.v_symbol.len); break; case BSON_TYPE_CODEWSCOPE: - if (bson_init_static (&local, value->value.v_codewscope.scope_data, value->value.v_codewscope.scope_len)) { - ret = bson_append_code_with_scope (bson, key, key_length, value->value.v_codewscope.code, &local); - bson_destroy (&local); + if (bson_init_static(&local, value->value.v_codewscope.scope_data, value->value.v_codewscope.scope_len)) { + ret = bson_append_code_with_scope(bson, key, key_length, value->value.v_codewscope.code, &local); + bson_destroy(&local); } break; case BSON_TYPE_INT32: - ret = bson_append_int32 (bson, key, key_length, value->value.v_int32); + ret = bson_append_int32(bson, key, key_length, value->value.v_int32); break; case BSON_TYPE_TIMESTAMP: - ret = bson_append_timestamp ( + ret = bson_append_timestamp( bson, key, key_length, value->value.v_timestamp.timestamp, value->value.v_timestamp.increment); break; case BSON_TYPE_INT64: - ret = bson_append_int64 (bson, key, key_length, value->value.v_int64); + ret = bson_append_int64(bson, key, key_length, value->value.v_int64); break; case BSON_TYPE_DECIMAL128: - ret = bson_append_decimal128 (bson, key, key_length, &(value->value.v_decimal128)); + ret = bson_append_decimal128(bson, key, key_length, &(value->value.v_decimal128)); break; case BSON_TYPE_MAXKEY: - ret = bson_append_maxkey (bson, key, key_length); + ret = bson_append_maxkey(bson, key, key_length); break; case BSON_TYPE_MINKEY: - ret = bson_append_minkey (bson, key, key_length); + ret = bson_append_minkey(bson, key, key_length); break; case BSON_TYPE_EOD: default: @@ -1755,16 +1854,13 @@ bson_append_value (bson_t *bson, const char *key, int key_length, const bson_val void -bson_init (bson_t *bson) +bson_init(bson_t *bson) { - bson_impl_inline_t *impl = (bson_impl_inline_t *) bson; + bson_impl_inline_t *impl = (bson_impl_inline_t *)bson; - BSON_ASSERT (bson); + BSON_ASSERT(bson); -#ifdef BSON_MEMCHECK - impl->canary = bson_malloc (1); -#endif - impl->flags = BSON_FLAG_INLINE | BSON_FLAG_STATIC; + impl->flags = BSON_FLAG_INLINE_DATA | BSON_FLAG_NO_FREE_OBJECT; impl->len = 5; impl->data[0] = 5; impl->data[1] = 0; @@ -1775,13 +1871,13 @@ bson_init (bson_t *bson) void -bson_reinit (bson_t *bson) +bson_reinit(bson_t *bson) { uint8_t *data; - BSON_ASSERT (bson); + BSON_ASSERT(bson); - data = _bson_data (bson); + data = _bson_data(bson); bson->len = 5; @@ -1794,21 +1890,20 @@ bson_reinit (bson_t *bson) bool -bson_init_static (bson_t *bson, const uint8_t *data, size_t length) +bson_init_static(bson_t *bson, const uint8_t *data, size_t length) { - bson_impl_alloc_t *impl = (bson_impl_alloc_t *) bson; - uint32_t len_le; + bson_impl_alloc_t *impl = (bson_impl_alloc_t *)bson; - BSON_ASSERT (bson); - BSON_ASSERT (data); + BSON_ASSERT(bson); + BSON_ASSERT(data); if ((length < 5) || (length > BSON_MAX_SIZE)) { return false; } - memcpy (&len_le, data, sizeof (len_le)); + const uint32_t hdr_len = mlib_read_u32le(data); - if ((size_t) BSON_UINT32_FROM_LE (len_le) != length) { + if (hdr_len != length) { return false; } @@ -1816,15 +1911,15 @@ bson_init_static (bson_t *bson, const uint8_t *data, size_t length) return false; } - impl->flags = BSON_FLAG_STATIC | BSON_FLAG_RDONLY; - impl->len = (uint32_t) length; + impl->flags = BSON_FLAG_NO_FREE_OBJECT | BSON_FLAG_RDONLY; + impl->len = (uint32_t)length; impl->parent = NULL; impl->depth = 0; - impl->buf = &impl->alloc; - impl->buflen = &impl->alloclen; + impl->indirect_buffer = NULL; + impl->indirect_buflen = NULL; impl->offset = 0; - impl->alloc = (uint8_t *) data; - impl->alloclen = length; + impl->own_buffer = (uint8_t *)data; + impl->own_buflen = length; impl->realloc = NULL; impl->realloc_func_ctx = NULL; @@ -1833,19 +1928,16 @@ bson_init_static (bson_t *bson, const uint8_t *data, size_t length) bson_t * -bson_new (void) +bson_new(void) { bson_impl_inline_t *impl; bson_t *bson; - bson = BSON_ALIGNED_ALLOC (bson_t); + bson = BSON_ALIGNED_ALLOC(bson_t); - impl = (bson_impl_inline_t *) bson; - impl->flags = BSON_FLAG_INLINE; + impl = (bson_impl_inline_t *)bson; + impl->flags = BSON_FLAG_INLINE_DATA; impl->len = 5; -#ifdef BSON_MEMCHECK - impl->canary = bson_malloc (1); -#endif impl->data[0] = 5; impl->data[1] = 0; impl->data[2] = 0; @@ -1857,36 +1949,36 @@ bson_new (void) bson_t * -bson_sized_new (size_t size) +bson_sized_new(size_t size) { bson_impl_alloc_t *impl_a; bson_t *b; - BSON_ASSERT (size <= BSON_MAX_SIZE); + BSON_ASSERT(size <= BSON_MAX_SIZE); { - b = BSON_ALIGNED_ALLOC (bson_t); - 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); - b->flags &= ~BSON_FLAG_STATIC; + bson_init(b); + b->flags &= ~BSON_FLAG_NO_FREE_OBJECT; } else { impl_a->flags = BSON_FLAG_NONE; impl_a->len = 5; impl_a->parent = NULL; impl_a->depth = 0; - impl_a->buf = &impl_a->alloc; - impl_a->buflen = &impl_a->alloclen; + impl_a->indirect_buffer = NULL; + impl_a->indirect_buflen = NULL; impl_a->offset = 0; - impl_a->alloclen = BSON_MAX (5, size); - impl_a->alloc = bson_malloc (impl_a->alloclen); - impl_a->alloc[0] = 5; - impl_a->alloc[1] = 0; - impl_a->alloc[2] = 0; - impl_a->alloc[3] = 0; - impl_a->alloc[4] = 0; + impl_a->own_buflen = BSON_MAX(5, size); + impl_a->own_buffer = bson_malloc(impl_a->own_buflen); + impl_a->own_buffer[0] = 5; + impl_a->own_buffer[1] = 0; + impl_a->own_buffer[2] = 0; + impl_a->own_buffer[3] = 0; + impl_a->own_buffer[4] = 0; impl_a->realloc = bson_realloc_ctx; impl_a->realloc_func_ctx = NULL; } @@ -1896,75 +1988,74 @@ bson_sized_new (size_t size) bson_t * -bson_new_from_data (const uint8_t *data, size_t length) +bson_new_from_data(const uint8_t *data, size_t length) { - uint32_t len_le; - bson_t *bson; - - BSON_ASSERT (data); + BSON_ASSERT(data); if ((length < 5) || (length > BSON_MAX_SIZE) || data[length - 1]) { + // Invalid length, or not null-terminated return NULL; } - memcpy (&len_le, data, sizeof (len_le)); + const int32_t hdr = mlib_read_i32le(data); - if (length != (size_t) BSON_UINT32_FROM_LE (len_le)) { + if (mlib_cmp(hdr, !=, length)) { + // Header's declared length is not equal to the length of the data buffer we were given return NULL; } - bson = bson_sized_new (length); - memcpy (_bson_data (bson), data, length); - bson->len = (uint32_t) length; + bson_t *const bson = bson_sized_new(length); + memcpy(_bson_data(bson), data, length); + bson->len = (uint32_t)length; return bson; } bson_t * -bson_new_from_buffer (uint8_t **buf, size_t *buf_len, bson_realloc_func realloc_func, void *realloc_func_ctx) +bson_new_from_buffer(uint8_t **buf, size_t *buf_len, bson_realloc_func realloc_func, void *realloc_func_ctx) { bson_impl_alloc_t *impl; - uint32_t len_le; uint32_t length; bson_t *bson; - BSON_ASSERT (buf); - BSON_ASSERT (buf_len); + BSON_ASSERT(buf); + BSON_ASSERT(buf_len); if (!realloc_func) { realloc_func = bson_realloc_ctx; } - bson = BSON_ALIGNED_ALLOC0 (bson_t); - impl = (bson_impl_alloc_t *) bson; + bson = BSON_ALIGNED_ALLOC0(bson_t); + impl = (bson_impl_alloc_t *)bson; if (!*buf) { length = 5; - len_le = BSON_UINT32_TO_LE (length); *buf_len = 5; - *buf = realloc_func (*buf, *buf_len, realloc_func_ctx); - memcpy (*buf, &len_le, sizeof (len_le)); + *buf = realloc_func(*buf, *buf_len, realloc_func_ctx); + mlib_write_u32le(*buf, length); (*buf)[4] = '\0'; } else { if ((*buf_len < 5) || (*buf_len > BSON_MAX_SIZE)) { - bson_free (bson); + bson_free(bson); + return NULL; + } + length = mlib_read_u32le(*buf); + if (length < 5 || length > *buf_len) { + bson_free(bson); return NULL; } - - memcpy (&len_le, *buf, sizeof (len_le)); - length = BSON_UINT32_FROM_LE (len_le); } if ((*buf)[length - 1]) { - bson_free (bson); + bson_free(bson); return NULL; } - impl->flags = BSON_FLAG_NO_FREE; + impl->flags = BSON_FLAG_NO_FREE_DATA; impl->len = length; - impl->buf = buf; - impl->buflen = buf_len; + impl->indirect_buffer = buf; + impl->indirect_buflen = buf_len; impl->realloc = realloc_func; impl->realloc_func_ctx = realloc_func_ctx; @@ -1973,94 +2064,89 @@ bson_new_from_buffer (uint8_t **buf, size_t *buf_len, bson_realloc_func realloc_ bson_t * -bson_copy (const bson_t *bson) +bson_copy(const bson_t *bson) { const uint8_t *data; - BSON_ASSERT (bson); + BSON_ASSERT(bson); - data = _bson_data (bson); - return bson_new_from_data (data, bson->len); + data = _bson_data(bson); + return bson_new_from_data(data, bson->len); } void -bson_copy_to (const bson_t *src, bson_t *dst) +bson_copy_to(const bson_t *src, bson_t *dst) { const uint8_t *data; bson_impl_alloc_t *adst; size_t len; - BSON_ASSERT (src); - BSON_ASSERT (dst); + BSON_ASSERT(src); + BSON_ASSERT(dst); - if ((src->flags & BSON_FLAG_INLINE)) { -#ifdef BSON_MEMCHECK - dst->len = src->len; - dst->canary = bson_malloc (1); - memcpy (dst->padding, src->padding, sizeof dst->padding); -#else - memcpy (dst, src, sizeof *dst); -#endif - dst->flags = (BSON_FLAG_STATIC | BSON_FLAG_INLINE); + if ((src->flags & BSON_FLAG_INLINE_DATA)) { + memcpy(dst, src, sizeof *dst); + dst->flags = (BSON_FLAG_NO_FREE_OBJECT | BSON_FLAG_INLINE_DATA); return; } - data = _bson_data (src); - len = bson_next_power_of_two ((size_t) src->len); + data = _bson_data(src); + len = _bson_round_up_alloc_size((size_t)src->len); + MONGOC_DEBUG_ASSERT(len <= BSON_MAX_SIZE); - adst = (bson_impl_alloc_t *) dst; - adst->flags = BSON_FLAG_STATIC; + adst = (bson_impl_alloc_t *)dst; + adst->flags = BSON_FLAG_NO_FREE_OBJECT; adst->len = src->len; adst->parent = NULL; adst->depth = 0; - adst->buf = &adst->alloc; - adst->buflen = &adst->alloclen; + adst->indirect_buffer = NULL; + adst->indirect_buflen = NULL; adst->offset = 0; - adst->alloc = bson_malloc (len); - adst->alloclen = len; + adst->own_buffer = bson_malloc(len); + adst->own_buflen = len; adst->realloc = bson_realloc_ctx; adst->realloc_func_ctx = NULL; - memcpy (adst->alloc, data, src->len); + memcpy(adst->own_buffer, data, src->len); } static bool -should_ignore (const char *first_exclude, va_list args, const char *name) +should_ignore(const char *first_exclude, va_list args, const char *name) { bool ret = false; const char *exclude = first_exclude; va_list args_copy; - va_copy (args_copy, args); + va_copy(args_copy, args); do { - if (!strcmp (name, exclude)) { + if (!strcmp(name, exclude)) { ret = true; break; } - } while ((exclude = va_arg (args_copy, const char *))); + } while ((exclude = va_arg(args_copy, const char *))); - va_end (args_copy); + va_end(args_copy); return ret; } void -bson_copy_to_excluding_noinit_va (const bson_t *src, bson_t *dst, const char *first_exclude, va_list args) +bson_copy_to_excluding_noinit_va(const bson_t *src, bson_t *dst, const char *first_exclude, va_list args) { bson_iter_t iter; - if (bson_iter_init (&iter, src)) { - while (bson_iter_next (&iter)) { - if (!should_ignore (first_exclude, args, bson_iter_key (&iter))) { - if (!bson_append_iter (dst, NULL, 0, &iter)) { + if (bson_iter_init(&iter, src)) { + while (bson_iter_next(&iter)) { + if (!should_ignore(first_exclude, args, bson_iter_key(&iter))) { + if (!bson_append_iter(dst, NULL, 0, &iter)) { /* * This should not be able to happen since we are copying * from within a valid bson_t. */ - BSON_ASSERT (false); + BSON_ASSERT(false); return; } } @@ -2070,120 +2156,113 @@ bson_copy_to_excluding_noinit_va (const bson_t *src, bson_t *dst, const char *fi void -bson_copy_to_excluding (const bson_t *src, bson_t *dst, const char *first_exclude, ...) -{ - va_list args; - - BSON_ASSERT (src); - BSON_ASSERT (dst); - BSON_ASSERT (first_exclude); - - bson_init (dst); - - va_start (args, first_exclude); - bson_copy_to_excluding_noinit_va (src, dst, first_exclude, args); - va_end (args); -} - -void -bson_copy_to_excluding_noinit (const bson_t *src, bson_t *dst, const char *first_exclude, ...) +bson_copy_to_excluding_noinit(const bson_t *src, bson_t *dst, const char *first_exclude, ...) { va_list args; - BSON_ASSERT (src); - BSON_ASSERT (dst); - BSON_ASSERT (first_exclude); + BSON_ASSERT(src); + BSON_ASSERT(dst); + BSON_ASSERT(first_exclude); - va_start (args, first_exclude); - bson_copy_to_excluding_noinit_va (src, dst, first_exclude, args); - va_end (args); + va_start(args, first_exclude); + bson_copy_to_excluding_noinit_va(src, dst, first_exclude, args); + va_end(args); } void -bson_destroy (bson_t *bson) +bson_destroy(bson_t *bson) { if (!bson) { return; } - if (!(bson->flags & (BSON_FLAG_RDONLY | BSON_FLAG_INLINE | BSON_FLAG_NO_FREE))) { - bson_free (*((bson_impl_alloc_t *) bson)->buf); - } - -#ifdef BSON_MEMCHECK - if (bson->flags & BSON_FLAG_INLINE) { - bson_free (bson->canary); + if (!(bson->flags & (BSON_FLAG_RDONLY | BSON_FLAG_INLINE_DATA | BSON_FLAG_NO_FREE_DATA))) { + bson_impl_alloc_t *const a = (bson_impl_alloc_t *)bson; + if (a->indirect_buffer) { + bson_free(*a->indirect_buffer); + } else { + bson_free(a->own_buffer); + } } -#endif - if (!(bson->flags & BSON_FLAG_STATIC)) { - bson_free (bson); + if (!(bson->flags & BSON_FLAG_NO_FREE_OBJECT)) { + bson_free(bson); } } uint8_t * -bson_reserve_buffer (bson_t *bson, uint32_t size) +bson_reserve_buffer(bson_t *bson, uint32_t total_size) { if (bson->flags & (BSON_FLAG_CHILD | BSON_FLAG_IN_CHILD | BSON_FLAG_RDONLY)) { return NULL; } - if (!_bson_grow (bson, size)) { - return NULL; + if (total_size > bson->len) { + if ((size_t)total_size > BSON_MAX_SIZE) { + return NULL; + } + + /* Note that the bson_t can also include space for parent or sibling documents (offset) and for trailing bytes + * (depth). These sizes will be considered by _bson_grow() but we can assume they are zero in documents without + * BSON_FLAG_CHILD or BSON_FLAG_IN_CHILD. If this is called on a document that's part of a bson_writer_t, it is + * correct to ignore offset: we set the size of the current document, leaving previous documents alone. */ + if (!_bson_grow(bson, total_size - bson->len)) { + // Will fail due to overflow or when reallocation is needed on a buffer that does not support it. + return NULL; + } } - if (bson->flags & BSON_FLAG_INLINE) { + if (bson->flags & BSON_FLAG_INLINE_DATA) { /* bson_grow didn't spill over */ - ((bson_impl_inline_t *) bson)->len = size; + ((bson_impl_inline_t *)bson)->len = total_size; + BSON_ASSERT(total_size <= BSON_INLINE_DATA_SIZE); } else { - ((bson_impl_alloc_t *) bson)->len = size; + bson_impl_alloc_t *impl = (bson_impl_alloc_t *)bson; + impl->len = total_size; + const size_t sz = impl->indirect_buffer ? *impl->indirect_buflen : impl->own_buflen; + (void)sz; + BSON_ASSERT(impl->offset <= sz && sz - impl->offset >= (size_t)total_size); } - return _bson_data (bson); + return _bson_data(bson); } bool -bson_steal (bson_t *dst, bson_t *src) +bson_steal(bson_t *dst, bson_t *src) { bson_impl_inline_t *src_inline; bson_impl_inline_t *dst_inline; bson_impl_alloc_t *alloc; - BSON_ASSERT (dst); - BSON_ASSERT (src); + BSON_ASSERT(dst); + BSON_ASSERT(src); - bson_init (dst); + bson_init(dst); if (src->flags & (BSON_FLAG_CHILD | BSON_FLAG_IN_CHILD | BSON_FLAG_RDONLY)) { return false; } - if (src->flags & BSON_FLAG_INLINE) { - src_inline = (bson_impl_inline_t *) src; - dst_inline = (bson_impl_inline_t *) dst; + if (src->flags & BSON_FLAG_INLINE_DATA) { + src_inline = (bson_impl_inline_t *)src; + dst_inline = (bson_impl_inline_t *)dst; dst_inline->len = src_inline->len; - memcpy (dst_inline->data, src_inline->data, sizeof src_inline->data); + memcpy(dst_inline->data, src_inline->data, sizeof src_inline->data); /* for consistency, src is always invalid after steal, even if inline */ src->len = 0; -#ifdef BSON_MEMCHECK - bson_free (src->canary); -#endif } else { -#ifdef BSON_MEMCHECK - bson_free (dst->canary); -#endif - memcpy (dst, src, sizeof (bson_t)); - alloc = (bson_impl_alloc_t *) dst; - alloc->flags |= BSON_FLAG_STATIC; - alloc->buf = &alloc->alloc; - alloc->buflen = &alloc->alloclen; + memcpy(dst, src, sizeof(bson_t)); + alloc = (bson_impl_alloc_t *)dst; + alloc->flags |= BSON_FLAG_NO_FREE_OBJECT; + alloc->indirect_buffer = NULL; + alloc->indirect_buflen = NULL; } - if (!(src->flags & BSON_FLAG_STATIC)) { - bson_free (src); + if (!(src->flags & BSON_FLAG_NO_FREE_OBJECT)) { + bson_free(src); } else { /* src is invalid after steal */ src->len = 0; @@ -2194,62 +2273,63 @@ bson_steal (bson_t *dst, bson_t *src) uint8_t * -bson_destroy_with_steal (bson_t *bson, bool steal, uint32_t *length) +bson_destroy_with_steal(bson_t *bson, bool steal, uint32_t *length) { uint8_t *ret = NULL; - BSON_ASSERT (bson); + BSON_ASSERT(bson); if (length) { *length = bson->len; } if (!steal) { - bson_destroy (bson); + bson_destroy(bson); return NULL; } if ((bson->flags & (BSON_FLAG_CHILD | BSON_FLAG_IN_CHILD | BSON_FLAG_RDONLY))) { /* Do nothing */ - } else if ((bson->flags & BSON_FLAG_INLINE)) { + } else if ((bson->flags & BSON_FLAG_INLINE_DATA)) { bson_impl_inline_t *inl; - inl = (bson_impl_inline_t *) bson; - ret = bson_malloc (bson->len); - memcpy (ret, inl->data, bson->len); + inl = (bson_impl_inline_t *)bson; + ret = bson_malloc(bson->len); + memcpy(ret, inl->data, bson->len); } else { - bson_impl_alloc_t *alloc; - - alloc = (bson_impl_alloc_t *) bson; - ret = *alloc->buf; - *alloc->buf = NULL; + bson_impl_alloc_t *const alloc = (bson_impl_alloc_t *)bson; + ret = alloc->indirect_buffer ? *alloc->indirect_buffer : alloc->own_buffer; + if (alloc->indirect_buffer) { + *alloc->indirect_buffer = NULL; + } + alloc->own_buffer = NULL; } - bson_destroy (bson); + bson_destroy(bson); return ret; } const uint8_t * -bson_get_data (const bson_t *bson) +bson_get_data(const bson_t *bson) { - BSON_ASSERT (bson); + BSON_ASSERT(bson); - return _bson_data (bson); + return _bson_data(bson); } uint32_t -bson_count_keys (const bson_t *bson) +bson_count_keys(const bson_t *bson) { uint32_t count = 0; bson_iter_t iter; - BSON_ASSERT (bson); + BSON_ASSERT(bson); - if (bson_iter_init (&iter, bson)) { - while (bson_iter_next (&iter)) { + if (bson_iter_init(&iter, bson)) { + while (bson_iter_next(&iter)) { count++; } } @@ -2259,24 +2339,24 @@ bson_count_keys (const bson_t *bson) bool -bson_has_field (const bson_t *bson, const char *key) +bson_has_field(const bson_t *bson, const char *key) { bson_iter_t iter; bson_iter_t child; - BSON_ASSERT (bson); - BSON_ASSERT (key); + BSON_ASSERT(bson); + BSON_ASSERT(key); - if (NULL != strchr (key, '.')) { - return (bson_iter_init (&iter, bson) && bson_iter_find_descendant (&iter, key, &child)); + if (NULL != strchr(key, '.')) { + return (bson_iter_init(&iter, bson) && bson_iter_find_descendant(&iter, key, &child)); } - return bson_iter_init_find (&iter, bson, key); + return bson_iter_init_find(&iter, bson, key); } int -bson_compare (const bson_t *bson, const bson_t *other) +bson_compare(const bson_t *bson, const bson_t *other) { const uint8_t *data1; const uint8_t *data2; @@ -2284,20 +2364,20 @@ bson_compare (const bson_t *bson, const bson_t *other) size_t len2; int64_t ret; - data1 = _bson_data (bson) + 4; + data1 = _bson_data(bson) + 4; len1 = bson->len - 4; - data2 = _bson_data (other) + 4; + data2 = _bson_data(other) + 4; len2 = other->len - 4; if (len1 == len2) { - return memcmp (data1, data2, len1); + return memcmp(data1, data2, len1); } - ret = memcmp (data1, data2, BSON_MIN (len1, len2)); + ret = memcmp(data1, data2, BSON_MIN(len1, len2)); if (ret == 0) { - ret = (int64_t) len1 - (int64_t) len2; + ret = (int64_t)len1 - (int64_t)len2; } return (ret < 0) ? -1 : (ret > 0); @@ -2305,1340 +2385,485 @@ bson_compare (const bson_t *bson, const bson_t *other) bool -bson_equal (const bson_t *bson, const bson_t *other) +bson_equal(const bson_t *bson, const bson_t *other) { - return !bson_compare (bson, other); + return !bson_compare(bson, other); } -static bool -_bson_as_json_visit_utf8 (const bson_iter_t *iter, const char *key, size_t v_utf8_len, const char *v_utf8, void *data) +char * +bson_as_json_with_opts(const bson_t *bson, size_t *length, const bson_json_opts_t *opts) { - bson_json_state_t *state = data; - char *escaped; + BSON_ASSERT_PARAM(bson); + BSON_OPTIONAL_PARAM(length); + BSON_ASSERT_PARAM(opts); - BSON_UNUSED (iter); - BSON_UNUSED (key); + // Convert the API-specified max length into a literal byte count; max length is transformed from a special value + // (-1) to the maximum representable size. + int32_t limit_i32 = opts->max_len; + uint32_t limit_u32 = 0; + if (limit_i32 == BSON_MAX_LEN_UNLIMITED) { + limit_u32 = UINT32_MAX - 1u; + } else if (limit_i32 > 0) { + limit_u32 = (uint32_t)limit_i32; + } - escaped = bson_utf8_escape_for_json (v_utf8, v_utf8_len); + // Use the bson length as an initial buffer capacity guess + mcommon_string_append_t append; + mcommon_string_set_append_with_limit(mcommon_string_new_with_capacity("", 0, bson->len), &append, limit_u32); - if (escaped) { - bson_string_append (state->str, "\""); - bson_string_append (state->str, escaped); - bson_string_append (state->str, "\""); - bson_free (escaped); - return false; + if (opts->is_outermost_array ? mcommon_json_append_bson_array(&append, bson, opts->mode, BSON_MAX_RECURSION) + : mcommon_json_append_bson_document(&append, bson, opts->mode, BSON_MAX_RECURSION)) { + if (length) { + *length = (size_t)mcommon_strlen_from_append(&append); + } + return mcommon_string_from_append_destroy_with_steal(&append); + } else { + if (length) { + *length = 0; + } + mcommon_string_from_append_destroy(&append); + return NULL; } - - return true; } -static bool -_bson_as_json_visit_int32 (const bson_iter_t *iter, const char *key, int32_t v_int32, void *data) +char * +bson_as_canonical_extended_json(const bson_t *bson, size_t *length) { - bson_json_state_t *state = data; - - BSON_UNUSED (iter); - BSON_UNUSED (key); + const bson_json_opts_t opts = {BSON_JSON_MODE_CANONICAL, BSON_MAX_LEN_UNLIMITED, false}; + return bson_as_json_with_opts(bson, length, &opts); +} - if (state->mode == BSON_JSON_MODE_CANONICAL) { - bson_string_append_printf (state->str, "{ \"$numberInt\" : \"%" PRId32 "\" }", v_int32); - } else { - bson_string_append_printf (state->str, "%" PRId32, v_int32); - } - return false; +char * +bson_as_legacy_extended_json(const bson_t *bson, size_t *length) +{ + const bson_json_opts_t opts = {BSON_JSON_MODE_LEGACY, BSON_MAX_LEN_UNLIMITED, false}; + return bson_as_json_with_opts(bson, length, &opts); } -static bool -_bson_as_json_visit_int64 (const bson_iter_t *iter, const char *key, int64_t v_int64, void *data) +char * +bson_as_relaxed_extended_json(const bson_t *bson, size_t *length) { - bson_json_state_t *state = data; - - BSON_UNUSED (iter); - BSON_UNUSED (key); + const bson_json_opts_t opts = {BSON_JSON_MODE_RELAXED, BSON_MAX_LEN_UNLIMITED, false}; + return bson_as_json_with_opts(bson, length, &opts); +} - if (state->mode == BSON_JSON_MODE_CANONICAL) { - bson_string_append_printf (state->str, "{ \"$numberLong\" : \"%" PRId64 "\" }", v_int64); - } else { - bson_string_append_printf (state->str, "%" PRId64, v_int64); - } - return false; -} - - -static bool -_bson_as_json_visit_decimal128 (const bson_iter_t *iter, const char *key, const bson_decimal128_t *value, void *data) -{ - 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\" : \""); - bson_string_append (state->str, decimal128_string); - bson_string_append (state->str, "\" }"); - - return false; -} - - -static bool -_bson_as_json_visit_double (const bson_iter_t *iter, const char *key, double v_double, void *data) -{ - bson_json_state_t *state = data; - bson_string_t *str = state->str; - 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. */ - legacy = state->mode == BSON_JSON_MODE_LEGACY || - (state->mode == BSON_JSON_MODE_RELAXED && !(v_double != v_double || v_double * 0 != 0)); - - if (!legacy) { - bson_string_append (state->str, "{ \"$numberDouble\" : \""); - } - - if (!legacy && v_double != v_double) { - bson_string_append (str, "NaN"); - } else if (!legacy && v_double * 0 != 0) { - if (v_double > 0) { - bson_string_append (str, "Infinity"); - } else { - bson_string_append (str, "-Infinity"); - } - } else { - start_len = str->len; - bson_string_append_printf (str, "%.20g", v_double); - - /* ensure trailing ".0" to distinguish "3" from "3.0" */ - if (strspn (&str->str[start_len], "0123456789-") == str->len - start_len) { - bson_string_append (str, ".0"); - } - } - - if (!legacy) { - bson_string_append (state->str, "\" }"); - } - - return false; -} - - -static bool -_bson_as_json_visit_undefined (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, "{ \"$undefined\" : true }"); - - return false; -} - - -static bool -_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; -} - - -static bool -_bson_as_json_visit_oid (const bson_iter_t *iter, const char *key, const bson_oid_t *oid, void *data) -{ - 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); - bson_string_append (state->str, "\" }"); - - return false; -} - - -static bool -_bson_as_json_visit_binary (const bson_iter_t *iter, - const char *key, - bson_subtype_t v_subtype, - size_t v_binary_len, - const uint8_t *v_binary, - void *data) -{ - bson_json_state_t *state = data; - size_t b64_len; - char *b64; - - BSON_UNUSED (iter); - BSON_UNUSED (key); - - b64_len = mcommon_b64_ntop_calculate_target_size (v_binary_len); - b64 = bson_malloc0 (b64_len); - 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) { - bson_string_append (state->str, "{ \"$binary\" : { \"base64\" : \""); - bson_string_append (state->str, b64); - bson_string_append (state->str, "\", \"subType\" : \""); - bson_string_append_printf (state->str, "%02x", v_subtype); - bson_string_append (state->str, "\" } }"); - } else { - bson_string_append (state->str, "{ \"$binary\" : \""); - bson_string_append (state->str, b64); - bson_string_append (state->str, "\", \"$type\" : \""); - bson_string_append_printf (state->str, "%02x", v_subtype); - bson_string_append (state->str, "\" }"); - } - - bson_free (b64); - - return false; -} - - -static bool -_bson_as_json_visit_bool (const bson_iter_t *iter, const char *key, bool v_bool, void *data) -{ - bson_json_state_t *state = data; - - BSON_UNUSED (iter); - BSON_UNUSED (key); - - bson_string_append (state->str, v_bool ? "true" : "false"); - - return false; -} - - -static bool -_bson_as_json_visit_date_time (const bson_iter_t *iter, const char *key, int64_t msec_since_epoch, void *data) -{ - 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\" : \""); - bson_string_append_printf (state->str, "%" PRId64, msec_since_epoch); - bson_string_append (state->str, "\" } }"); - } else if (state->mode == BSON_JSON_MODE_RELAXED) { - bson_string_append (state->str, "{ \"$date\" : \""); - _bson_iso8601_date_format (msec_since_epoch, state->str); - bson_string_append (state->str, "\" }"); - } else { - bson_string_append (state->str, "{ \"$date\" : "); - bson_string_append_printf (state->str, "%" PRId64, msec_since_epoch); - bson_string_append (state->str, " }"); - } - - return false; -} - - -static bool -_bson_as_json_visit_regex ( - const bson_iter_t *iter, const char *key, const char *v_regex, const char *v_options, void *data) -{ - 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; - } - - if (state->mode == BSON_JSON_MODE_CANONICAL || state->mode == BSON_JSON_MODE_RELAXED) { - bson_string_append (state->str, "{ \"$regularExpression\" : { \"pattern\" : \""); - bson_string_append (state->str, escaped); - bson_string_append (state->str, "\", \"options\" : \""); - _bson_append_regex_options_sorted (state->str, v_options); - bson_string_append (state->str, "\" } }"); - } else { - bson_string_append (state->str, "{ \"$regex\" : \""); - bson_string_append (state->str, escaped); - bson_string_append (state->str, "\", \"$options\" : \""); - _bson_append_regex_options_sorted (state->str, v_options); - bson_string_append (state->str, "\" }"); - } - - bson_free (escaped); - - return false; -} - - -static bool -_bson_as_json_visit_timestamp ( - const bson_iter_t *iter, const char *key, uint32_t v_timestamp, uint32_t v_increment, void *data) -{ - 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\" : "); - bson_string_append_printf (state->str, "%u", v_increment); - bson_string_append (state->str, " } }"); - - return false; -} - - -static bool -_bson_as_json_visit_dbpointer (const bson_iter_t *iter, - const char *key, - size_t v_collection_len, - const char *v_collection, - const bson_oid_t *v_oid, - void *data) -{ - bson_json_state_t *state = data; - 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; - } - - if (state->mode == BSON_JSON_MODE_CANONICAL || state->mode == BSON_JSON_MODE_RELAXED) { - bson_string_append (state->str, "{ \"$dbPointer\" : { \"$ref\" : \""); - bson_string_append (state->str, escaped); - bson_string_append (state->str, "\""); - - if (v_oid) { - bson_oid_to_string (v_oid, str); - bson_string_append (state->str, ", \"$id\" : { \"$oid\" : \""); - bson_string_append (state->str, str); - bson_string_append (state->str, "\" }"); - } - - bson_string_append (state->str, " } }"); - } else { - bson_string_append (state->str, "{ \"$ref\" : \""); - bson_string_append (state->str, escaped); - bson_string_append (state->str, "\""); - - if (v_oid) { - bson_oid_to_string (v_oid, str); - bson_string_append (state->str, ", \"$id\" : \""); - bson_string_append (state->str, str); - bson_string_append (state->str, "\""); - } - - bson_string_append (state->str, " }"); - } - - bson_free (escaped); - - return false; -} - - -static bool -_bson_as_json_visit_minkey (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, "{ \"$minKey\" : 1 }"); - - return false; -} - - -static bool -_bson_as_json_visit_maxkey (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, "{ \"$maxKey\" : 1 }"); - - return false; -} - - -static bool -_bson_as_json_visit_before (const bson_iter_t *iter, const char *key, void *data) -{ - bson_json_state_t *state = data; - char *escaped; - - BSON_UNUSED (iter); - - if (state->max_len_reached) { - return true; - } - - if (state->count) { - bson_string_append (state->str, ", "); - } - - if (state->keys) { - escaped = bson_utf8_escape_for_json (key, -1); - if (escaped) { - bson_string_append (state->str, "\""); - bson_string_append (state->str, escaped); - bson_string_append (state->str, "\" : "); - bson_free (escaped); - } else { - return true; - } - } - - state->count++; - - return false; -} - - -static bool -_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 (bson_cmp_greater_equal_us (state->str->len, state->max_len)) { - state->max_len_reached = true; - - 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, (uint32_t) state->max_len); - } - - return true; - } - - return false; -} - - -static void -_bson_as_json_visit_corrupt (const bson_iter_t *iter, void *data) -{ - *(((bson_json_state_t *) data)->err_offset) = iter->off; -} - - -static bool -_bson_as_json_visit_code (const bson_iter_t *iter, const char *key, size_t v_code_len, const char *v_code, void *data) -{ - 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; - } - - bson_string_append (state->str, "{ \"$code\" : \""); - bson_string_append (state->str, escaped); - bson_string_append (state->str, "\" }"); - bson_free (escaped); - - return false; -} - - -static bool -_bson_as_json_visit_symbol ( - const bson_iter_t *iter, const char *key, size_t v_symbol_len, const char *v_symbol, void *data) -{ - 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; - } - - if (state->mode == BSON_JSON_MODE_CANONICAL || state->mode == BSON_JSON_MODE_RELAXED) { - bson_string_append (state->str, "{ \"$symbol\" : \""); - bson_string_append (state->str, escaped); - bson_string_append (state->str, "\" }"); - } else { - bson_string_append (state->str, "\""); - bson_string_append (state->str, escaped); - bson_string_append (state->str, "\""); - } - - bson_free (escaped); - - return false; -} - - -static bool -_bson_as_json_visit_codewscope ( - const bson_iter_t *iter, const char *key, size_t v_code_len, const char *v_code, const bson_t *v_scope, void *data) -{ - bson_json_state_t *state = data; - char *code_escaped; - 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; - } - - bson_string_append (state->str, "{ \"$code\" : \""); - bson_string_append (state->str, code_escaped); - bson_string_append (state->str, "\", \"$scope\" : "); - - bson_free (code_escaped); - - /* Encode scope with the same mode */ - if (state->max_len != BSON_MAX_LEN_UNLIMITED) { - 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, false); - - if (!scope) { - return true; - } - - bson_string_append (state->str, scope); - bson_string_append (state->str, " }"); - - bson_free (scope); - - return false; -} - - -static const bson_visitor_t bson_as_json_visitors = { - _bson_as_json_visit_before, _bson_as_json_visit_after, _bson_as_json_visit_corrupt, - _bson_as_json_visit_double, _bson_as_json_visit_utf8, _bson_as_json_visit_document, - _bson_as_json_visit_array, _bson_as_json_visit_binary, _bson_as_json_visit_undefined, - _bson_as_json_visit_oid, _bson_as_json_visit_bool, _bson_as_json_visit_date_time, - _bson_as_json_visit_null, _bson_as_json_visit_regex, _bson_as_json_visit_dbpointer, - _bson_as_json_visit_code, _bson_as_json_visit_symbol, _bson_as_json_visit_codewscope, - _bson_as_json_visit_int32, _bson_as_json_visit_timestamp, _bson_as_json_visit_int64, - _bson_as_json_visit_maxkey, _bson_as_json_visit_minkey, NULL, /* visit_unsupported_type */ - _bson_as_json_visit_decimal128, -}; - - -static bool -_bson_as_json_visit_document (const bson_iter_t *iter, const char *key, const bson_t *v_document, void *data) -{ - bson_json_state_t *state = data; - 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; - } - - if (bson_iter_init (&child, v_document)) { - child_state.str = bson_string_new ("{ "); - child_state.depth = state->depth + 1; - child_state.mode = state->mode; - child_state.max_len = BSON_MAX_LEN_UNLIMITED; - if (state->max_len != BSON_MAX_LEN_UNLIMITED) { - 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; - - if (bson_iter_visit_all (&child, &bson_as_json_visitors, &child_state)) { - if (child_state.max_len_reached) { - bson_string_append (state->str, child_state.str->str); - } - - bson_string_free (child_state.str, true); - - /* If max_len was reached, we return a success state to ensure that - * VISIT_AFTER is still called - */ - return !child_state.max_len_reached; - } - - bson_string_append (child_state.str, " }"); - bson_string_append (state->str, child_state.str->str); - bson_string_free (child_state.str, true); - } - - return false; -} - - -static bool -_bson_as_json_visit_array (const bson_iter_t *iter, const char *key, const bson_t *v_array, void *data) -{ - bson_json_state_t *state = data; - 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; - } - - if (bson_iter_init (&child, v_array)) { - child_state.str = bson_string_new ("[ "); - child_state.depth = state->depth + 1; - child_state.mode = state->mode; - child_state.max_len = BSON_MAX_LEN_UNLIMITED; - if (state->max_len != BSON_MAX_LEN_UNLIMITED) { - 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; - - if (bson_iter_visit_all (&child, &bson_as_json_visitors, &child_state)) { - if (child_state.max_len_reached) { - bson_string_append (state->str, child_state.str->str); - } - - bson_string_free (child_state.str, true); - - /* If max_len was reached, we return a success state to ensure that - * VISIT_AFTER is still called - */ - return !child_state.max_len_reached; - } - - bson_string_append (child_state.str, " ]"); - bson_string_append (state->str, child_state.str->str); - bson_string_free (child_state.str, true); - } - - return false; -} - - -static char * -_bson_as_json_visit_all ( - const bson_t *bson, size_t *length, bson_json_mode_t mode, int32_t max_len, bool is_outermost_array) -{ - bson_json_state_t state; - bson_iter_t iter; - ssize_t err_offset = -1; - int32_t remaining; - - BSON_ASSERT (bson); - - if (length) { - *length = 0; - } - - if (bson_empty0 (bson)) { - if (length) { - *length = 3; - } - - return bson_strdup (is_outermost_array ? "[ ]" : "{ }"); - } - - if (!bson_iter_init (&iter, bson)) { - return NULL; - } - - state.count = 0; - state.keys = !is_outermost_array; - state.str = bson_string_new (is_outermost_array ? "[ " : "{ "); - state.depth = 0; - state.err_offset = &err_offset; - state.mode = mode; - state.max_len = max_len; - state.max_len_reached = false; - - 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; - } - - /* 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, is_outermost_array ? " ]" : " }"); - } else if (remaining == 1) { - bson_string_append (state.str, " "); - } - - if (length) { - *length = state.str->len; - } - - return bson_string_free (state.str, false); -} - - -char * -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, 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, false}; - return bson_as_json_with_opts (bson, length, &opts); -} - - -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, false}; - return bson_as_json_with_opts (bson, length, &opts); -} - - -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, false}; - return bson_as_json_with_opts (bson, length, &opts); -} - - -char * -bson_array_as_json (const bson_t *bson, size_t *length) -{ - const bson_json_opts_t opts = {BSON_JSON_MODE_LEGACY, BSON_MAX_LEN_UNLIMITED, true}; - return bson_as_json_with_opts (bson, length, &opts); +char * +bson_array_as_legacy_extended_json(const bson_t *bson, size_t *length) +{ + const bson_json_opts_t opts = {BSON_JSON_MODE_LEGACY, BSON_MAX_LEN_UNLIMITED, true}; + return bson_as_json_with_opts(bson, length, &opts); } char * -bson_array_as_relaxed_extended_json (const bson_t *bson, size_t *length) +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); + return bson_as_json_with_opts(bson, length, &opts); } char * -bson_array_as_canonical_extended_json (const bson_t *bson, size_t *length) +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); + return bson_as_json_with_opts(bson, length, &opts); } -#define VALIDATION_ERR(_flag, _msg, ...) bson_set_error (&state->error, BSON_ERROR_INVALID, _flag, _msg, __VA_ARGS__) - -static bool -_bson_iter_validate_utf8 (const bson_iter_t *iter, const char *key, size_t v_utf8_len, const char *v_utf8, void *data) +bool +bson_validate(const bson_t *bson, bson_validate_flags_t flags, size_t *offset) { - bson_validate_state_t *state = data; - bool allow_null; - - if ((state->flags & BSON_VALIDATE_UTF8)) { - allow_null = !!(state->flags & BSON_VALIDATE_UTF8_ALLOW_NULL); - - if (!bson_utf8_validate (v_utf8, v_utf8_len, allow_null)) { - state->err_offset = iter->off; - VALIDATION_ERR (BSON_VALIDATE_UTF8, "invalid utf8 string for key \"%s\"", key); - return true; - } - } - - if ((state->flags & BSON_VALIDATE_DOLLAR_KEYS)) { - if (state->phase == BSON_VALIDATE_PHASE_LF_REF_UTF8) { - state->phase = BSON_VALIDATE_PHASE_LF_ID_KEY; - } else if (state->phase == BSON_VALIDATE_PHASE_LF_DB_UTF8) { - state->phase = BSON_VALIDATE_PHASE_NOT_DBREF; - } - } - - return false; + return bson_validate_with_error_and_offset(bson, flags, offset, NULL); } -static void -_bson_iter_validate_corrupt (const bson_iter_t *iter, void *data) +bool +bson_validate_with_error(const bson_t *bson, bson_validate_flags_t flags, bson_error_t *error) { - bson_validate_state_t *state = data; - - state->err_offset = iter->err_off; - VALIDATION_ERR (BSON_VALIDATE_NONE, "%s", "corrupt BSON"); + return bson_validate_with_error_and_offset(bson, flags, NULL, error); } - -static bool -_bson_iter_validate_before (const bson_iter_t *iter, const char *key, void *data) +bool +bson_validate_with_error_and_offset(const bson_t *bson, + bson_validate_flags_t flags, + size_t *offset, + bson_error_t *error) { - bson_validate_state_t *state = data; + BSON_ASSERT_PARAM(bson); + BSON_OPTIONAL_PARAM(offset); + BSON_OPTIONAL_PARAM(error); - if ((state->flags & BSON_VALIDATE_EMPTY_KEYS)) { - if (key[0] == '\0') { - state->err_offset = iter->off; - VALIDATION_ERR (BSON_VALIDATE_EMPTY_KEYS, "%s", "empty key"); - return true; - } + size_t offset_local = 0; + if (!offset) { + offset = &offset_local; } - - if ((state->flags & BSON_VALIDATE_DOLLAR_KEYS)) { - if (key[0] == '$') { - if (state->phase == BSON_VALIDATE_PHASE_LF_REF_KEY && strcmp (key, "$ref") == 0) { - state->phase = BSON_VALIDATE_PHASE_LF_REF_UTF8; - } else if (state->phase == BSON_VALIDATE_PHASE_LF_ID_KEY && strcmp (key, "$id") == 0) { - state->phase = BSON_VALIDATE_PHASE_LF_DB_KEY; - } else if (state->phase == BSON_VALIDATE_PHASE_LF_DB_KEY && strcmp (key, "$db") == 0) { - state->phase = BSON_VALIDATE_PHASE_LF_DB_UTF8; - } else { - state->err_offset = iter->off; - VALIDATION_ERR (BSON_VALIDATE_DOLLAR_KEYS, "keys cannot begin with \"$\": \"%s\"", key); - return true; - } - } else if (state->phase == BSON_VALIDATE_PHASE_LF_ID_KEY || state->phase == BSON_VALIDATE_PHASE_LF_REF_UTF8 || - state->phase == BSON_VALIDATE_PHASE_LF_DB_UTF8) { - state->err_offset = iter->off; - VALIDATION_ERR (BSON_VALIDATE_DOLLAR_KEYS, "invalid key within DBRef subdocument: \"%s\"", key); - return true; - } else { - state->phase = BSON_VALIDATE_PHASE_NOT_DBREF; - } - } - - if ((state->flags & BSON_VALIDATE_DOT_KEYS)) { - if (strstr (key, ".")) { - state->err_offset = iter->off; - VALIDATION_ERR (BSON_VALIDATE_DOT_KEYS, "keys cannot contain \".\": \"%s\"", key); - return true; - } + bson_error_t error_local; + if (!error) { + error = &error_local; } - return false; + return _bson_validate_impl_v2(bson, flags, offset, error); } -static bool -_bson_iter_validate_codewscope ( - const bson_iter_t *iter, const char *key, size_t v_code_len, const char *v_code, const bson_t *v_scope, void *data) -{ - 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"); - return false; - } - - return true; -} - - -static bool -_bson_iter_validate_document (const bson_iter_t *iter, const char *key, const bson_t *v_document, void *data); - - -static const bson_visitor_t bson_validate_funcs = { - _bson_iter_validate_before, - NULL, /* visit_after */ - _bson_iter_validate_corrupt, - NULL, /* visit_double */ - _bson_iter_validate_utf8, - _bson_iter_validate_document, - _bson_iter_validate_document, /* visit_array */ - NULL, /* visit_binary */ - NULL, /* visit_undefined */ - NULL, /* visit_oid */ - NULL, /* visit_bool */ - NULL, /* visit_date_time */ - NULL, /* visit_null */ - NULL, /* visit_regex */ - NULL, /* visit_dbpoint */ - NULL, /* visit_code */ - NULL, /* visit_symbol */ - _bson_iter_validate_codewscope, -}; - - -static bool -_bson_iter_validate_document (const bson_iter_t *iter, const char *key, const bson_t *v_document, void *data) +bool +bson_concat(bson_t *dst, const bson_t *src) { - bson_validate_state_t *state = data; - bson_iter_t child; - bson_validate_phase_t phase = state->phase; + BSON_ASSERT(dst); + BSON_ASSERT(src); - BSON_UNUSED (key); + if (!bson_empty(src)) { + BSON_APPEND_BYTES_LIST_DECLARE(args); - if (!bson_iter_init (&child, v_document)) { - state->err_offset = iter->off; - return true; - } - - if (state->phase == BSON_VALIDATE_PHASE_START) { - state->phase = BSON_VALIDATE_PHASE_TOP; - } else { - state->phase = BSON_VALIDATE_PHASE_LF_REF_KEY; - } - - (void) bson_iter_visit_all (&child, &bson_validate_funcs, state); - - if (state->phase == BSON_VALIDATE_PHASE_LF_ID_KEY || state->phase == BSON_VALIDATE_PHASE_LF_REF_UTF8 || - state->phase == BSON_VALIDATE_PHASE_LF_DB_UTF8) { - if (state->err_offset <= 0) { - state->err_offset = iter->off; - } + BSON_APPEND_BYTES_ADD_ARGUMENT(args, _bson_data(src) + 4, src->len - 5u); - return true; + BSON_APPEND_BYTES_APPLY_ARGUMENTS(dst, args); } - state->phase = phase; + return true; +append_failure: return false; } +struct _bson_array_builder_t { + uint32_t index; + bson_t bson; +}; -static void -_bson_validate_internal (const bson_t *bson, bson_validate_state_t *state) +bson_array_builder_t * +bson_array_builder_new(void) { - bson_iter_t iter; - - state->err_offset = -1; - state->phase = BSON_VALIDATE_PHASE_START; - memset (&state->error, 0, sizeof state->error); - - if (!bson_iter_init (&iter, bson)) { - state->err_offset = 0; - VALIDATION_ERR (BSON_VALIDATE_NONE, "%s", "corrupt BSON"); - } else { - _bson_iter_validate_document (&iter, NULL, bson, state); - } + bson_array_builder_t *bab = BSON_ALIGNED_ALLOC0(bson_array_builder_t); + bson_init(&bab->bson); + return bab; } +// `bson_array_builder_append_impl` generates the next key index, calls +// `append_fn`, and may update the tracked next index. +#define bson_array_builder_append_impl(append_fn, ...) \ + if (1) { \ + BSON_ASSERT_PARAM(bab); \ + const char *key; \ + char buf[16]; \ + size_t key_length = bson_uint32_to_string(bab->index, &key, buf, sizeof buf); \ + /* Expect enough room in `buf` for key string. UINT32_MAX is 10 digits. \ + * With the NULL terminator, 11 is expected maximum number of \ + * characters. */ \ + BSON_ASSERT(key_length < sizeof buf); \ + bool ok = append_fn(&bab->bson, key, (int)key_length, __VA_ARGS__); \ + if (ok) { \ + bab->index += 1; \ + } \ + return ok; \ + } else \ + (void)0 -bool -bson_validate (const bson_t *bson, bson_validate_flags_t flags, size_t *offset) -{ - bson_validate_state_t state; - - state.flags = flags; - _bson_validate_internal (bson, &state); - - if (state.err_offset > 0 && offset) { - *offset = (size_t) state.err_offset; - } - - return state.err_offset < 0; -} - +#define bson_array_builder_append_impl_noargs(append_fn) \ + if (1) { \ + BSON_ASSERT_PARAM(bab); \ + const char *key; \ + char buf[16]; \ + size_t key_length = bson_uint32_to_string(bab->index, &key, buf, sizeof buf); \ + /* Expect enough room in `buf` for key string. UINT32_MAX is 10 digits. \ + * With the NULL terminator, 11 is expected maximum number of \ + * characters. */ \ + BSON_ASSERT(key_length < sizeof buf); \ + bool ok = append_fn(&bab->bson, key, (int)key_length); \ + if (ok) { \ + bab->index += 1; \ + } \ + return ok; \ + } else \ + (void)0 bool -bson_validate_with_error (const bson_t *bson, bson_validate_flags_t flags, bson_error_t *error) +bson_array_builder_append_value(bson_array_builder_t *bab, const bson_value_t *value) { - bson_validate_state_t state; - - state.flags = flags; - _bson_validate_internal (bson, &state); - - if (state.err_offset > 0 && error) { - memcpy (error, &state.error, sizeof *error); - } - - return state.err_offset < 0; + bson_array_builder_append_impl(bson_append_value, value); } bool -bson_concat (bson_t *dst, const bson_t *src) +bson_array_builder_append_array(bson_array_builder_t *bab, const bson_t *array) { - BSON_ASSERT (dst); - BSON_ASSERT (src); - - if (!bson_empty (src)) { - return _bson_append (dst, 1, src->len - 5, src->len - 5, _bson_data (src) + 4); - } - - return true; + bson_array_builder_append_impl(bson_append_array, array); } -struct _bson_array_builder_t { - uint32_t index; - bson_t bson; -}; - -bson_array_builder_t * -bson_array_builder_new (void) -{ - bson_array_builder_t *bab = BSON_ALIGNED_ALLOC0 (bson_array_builder_t); - bson_init (&bab->bson); - return bab; -} - -// `bson_array_builder_append_impl` generates the next key index, calls -// `append_fn`, and may update the tracked next index. -#define bson_array_builder_append_impl(append_fn, ...) \ - if (1) { \ - BSON_ASSERT_PARAM (bab); \ - const char *key; \ - char buf[16]; \ - size_t key_length = bson_uint32_to_string (bab->index, &key, buf, sizeof buf); \ - /* Expect enough room in `buf` for key string. UINT32_MAX is 10 digits. \ - * With the NULL terminator, 11 is expected maximum number of \ - * characters. */ \ - BSON_ASSERT (key_length < sizeof buf); \ - bool ok = append_fn (&bab->bson, key, (int) key_length, __VA_ARGS__); \ - if (ok) { \ - bab->index += 1; \ - } \ - return ok; \ - } else \ - (void) 0 - -#define bson_array_builder_append_impl_noargs(append_fn) \ - if (1) { \ - BSON_ASSERT_PARAM (bab); \ - const char *key; \ - char buf[16]; \ - size_t key_length = bson_uint32_to_string (bab->index, &key, buf, sizeof buf); \ - /* Expect enough room in `buf` for key string. UINT32_MAX is 10 digits. \ - * With the NULL terminator, 11 is expected maximum number of \ - * characters. */ \ - BSON_ASSERT (key_length < sizeof buf); \ - bool ok = append_fn (&bab->bson, key, (int) key_length); \ - if (ok) { \ - bab->index += 1; \ - } \ - return ok; \ - } else \ - (void) 0 bool -bson_array_builder_append_value (bson_array_builder_t *bab, const bson_value_t *value) +bson_array_builder_append_array_from_vector(bson_array_builder_t *bab, const bson_iter_t *iter) { - bson_array_builder_append_impl (bson_append_value, value); + bson_array_builder_append_impl(bson_append_array_from_vector, iter); } + bool -bson_array_builder_append_array (bson_array_builder_t *bab, const bson_t *array) +bson_array_builder_append_binary(bson_array_builder_t *bab, + bson_subtype_t subtype, + const uint8_t *binary, + uint32_t length) { - bson_array_builder_append_impl (bson_append_array, array); + bson_array_builder_append_impl(bson_append_binary, subtype, binary, length); } bool -bson_array_builder_append_binary (bson_array_builder_t *bab, - bson_subtype_t subtype, - const uint8_t *binary, - uint32_t length) +bson_array_builder_append_binary_uninit(bson_array_builder_t *bab, + bson_subtype_t subtype, + uint8_t **binary, + uint32_t length) { - bson_array_builder_append_impl (bson_append_binary, subtype, binary, length); + bson_array_builder_append_impl(bson_append_binary_uninit, subtype, binary, length); } bool -bson_array_builder_append_bool (bson_array_builder_t *bab, bool value) +bson_array_builder_append_bool(bson_array_builder_t *bab, bool value) { - bson_array_builder_append_impl (bson_append_bool, value); + bson_array_builder_append_impl(bson_append_bool, value); } bool -bson_array_builder_append_code (bson_array_builder_t *bab, const char *javascript) +bson_array_builder_append_code(bson_array_builder_t *bab, const char *javascript) { - bson_array_builder_append_impl (bson_append_code, javascript); + bson_array_builder_append_impl(bson_append_code, javascript); } bool -bson_array_builder_append_code_with_scope (bson_array_builder_t *bab, const char *javascript, const bson_t *scope) +bson_array_builder_append_code_with_scope(bson_array_builder_t *bab, const char *javascript, const bson_t *scope) { - bson_array_builder_append_impl (bson_append_code_with_scope, javascript, scope); + bson_array_builder_append_impl(bson_append_code_with_scope, javascript, scope); } bool -bson_array_builder_append_dbpointer (bson_array_builder_t *bab, const char *collection, const bson_oid_t *oid) +bson_array_builder_append_dbpointer(bson_array_builder_t *bab, const char *collection, const bson_oid_t *oid) { - bson_array_builder_append_impl (bson_append_dbpointer, collection, oid); + bson_array_builder_append_impl(bson_append_dbpointer, collection, oid); } bool -bson_array_builder_append_double (bson_array_builder_t *bab, double value) +bson_array_builder_append_double(bson_array_builder_t *bab, double value) { - bson_array_builder_append_impl (bson_append_double, value); + bson_array_builder_append_impl(bson_append_double, value); } bool -bson_array_builder_append_document (bson_array_builder_t *bab, const bson_t *value) +bson_array_builder_append_document(bson_array_builder_t *bab, const bson_t *value) { - bson_array_builder_append_impl (bson_append_document, value); + bson_array_builder_append_impl(bson_append_document, value); } bool -bson_array_builder_append_document_begin (bson_array_builder_t *bab, bson_t *child) +bson_array_builder_append_document_begin(bson_array_builder_t *bab, bson_t *child) { - bson_array_builder_append_impl (bson_append_document_begin, child); + bson_array_builder_append_impl(bson_append_document_begin, child); } bool -bson_array_builder_append_document_end (bson_array_builder_t *bab, bson_t *child) +bson_array_builder_append_document_end(bson_array_builder_t *bab, bson_t *child) { - return bson_append_document_end (&bab->bson, child); + return bson_append_document_end(&bab->bson, child); } bool -bson_array_builder_append_int32 (bson_array_builder_t *bab, int32_t value) +bson_array_builder_append_int32(bson_array_builder_t *bab, int32_t value) { - bson_array_builder_append_impl (bson_append_int32, value); + bson_array_builder_append_impl(bson_append_int32, value); } bool -bson_array_builder_append_int64 (bson_array_builder_t *bab, int64_t value) +bson_array_builder_append_int64(bson_array_builder_t *bab, int64_t value) { - bson_array_builder_append_impl (bson_append_int64, value); + bson_array_builder_append_impl(bson_append_int64, value); } bool -bson_array_builder_append_decimal128 (bson_array_builder_t *bab, const bson_decimal128_t *value) +bson_array_builder_append_decimal128(bson_array_builder_t *bab, const bson_decimal128_t *value) { - bson_array_builder_append_impl (bson_append_decimal128, value); + bson_array_builder_append_impl(bson_append_decimal128, value); } bool -bson_array_builder_append_iter (bson_array_builder_t *bab, const bson_iter_t *iter) +bson_array_builder_append_iter(bson_array_builder_t *bab, const bson_iter_t *iter) { - bson_array_builder_append_impl (bson_append_iter, iter); + bson_array_builder_append_impl(bson_append_iter, iter); } bool -bson_array_builder_append_minkey (bson_array_builder_t *bab) +bson_array_builder_append_minkey(bson_array_builder_t *bab) { - bson_array_builder_append_impl_noargs (bson_append_minkey); + bson_array_builder_append_impl_noargs(bson_append_minkey); } bool -bson_array_builder_append_maxkey (bson_array_builder_t *bab) +bson_array_builder_append_maxkey(bson_array_builder_t *bab) { - bson_array_builder_append_impl_noargs (bson_append_maxkey); + bson_array_builder_append_impl_noargs(bson_append_maxkey); } bool -bson_array_builder_append_null (bson_array_builder_t *bab) +bson_array_builder_append_null(bson_array_builder_t *bab) { - bson_array_builder_append_impl_noargs (bson_append_null); + bson_array_builder_append_impl_noargs(bson_append_null); } bool -bson_array_builder_append_oid (bson_array_builder_t *bab, const bson_oid_t *oid) +bson_array_builder_append_oid(bson_array_builder_t *bab, const bson_oid_t *oid) { - bson_array_builder_append_impl (bson_append_oid, oid); + bson_array_builder_append_impl(bson_append_oid, oid); } bool -bson_array_builder_append_regex (bson_array_builder_t *bab, const char *regex, const char *options) +bson_array_builder_append_regex(bson_array_builder_t *bab, const char *regex, const char *options) { - bson_array_builder_append_impl (bson_append_regex, regex, options); + bson_array_builder_append_impl(bson_append_regex, regex, options); } bool -bson_array_builder_append_regex_w_len (bson_array_builder_t *bab, - const char *regex, - int regex_length, - const char *options) +bson_array_builder_append_regex_w_len(bson_array_builder_t *bab, + const char *regex, + int regex_length, + const char *options) { - bson_array_builder_append_impl (bson_append_regex_w_len, regex, regex_length, options); + bson_array_builder_append_impl(bson_append_regex_w_len, regex, regex_length, options); } bool -bson_array_builder_append_utf8 (bson_array_builder_t *bab, const char *value, int length) +bson_array_builder_append_utf8(bson_array_builder_t *bab, const char *value, int length) { - bson_array_builder_append_impl (bson_append_utf8, value, length); + bson_array_builder_append_impl(bson_append_utf8, value, length); } bool -bson_array_builder_append_symbol (bson_array_builder_t *bab, const char *value, int length) +bson_array_builder_append_symbol(bson_array_builder_t *bab, const char *value, int length) { - bson_array_builder_append_impl (bson_append_symbol, value, length); + bson_array_builder_append_impl(bson_append_symbol, value, length); } bool -bson_array_builder_append_time_t (bson_array_builder_t *bab, time_t value) +bson_array_builder_append_time_t(bson_array_builder_t *bab, time_t value) { - bson_array_builder_append_impl (bson_append_time_t, value); + bson_array_builder_append_impl(bson_append_time_t, value); } bool -bson_array_builder_append_timeval (bson_array_builder_t *bab, struct timeval *value) +bson_array_builder_append_timeval(bson_array_builder_t *bab, struct timeval *value) { - bson_array_builder_append_impl (bson_append_timeval, value); + bson_array_builder_append_impl(bson_append_timeval, value); } bool -bson_array_builder_append_date_time (bson_array_builder_t *bab, int64_t value) +bson_array_builder_append_date_time(bson_array_builder_t *bab, int64_t value) { - bson_array_builder_append_impl (bson_append_date_time, value); + bson_array_builder_append_impl(bson_append_date_time, value); } bool -bson_array_builder_append_now_utc (bson_array_builder_t *bab) +bson_array_builder_append_now_utc(bson_array_builder_t *bab) { - bson_array_builder_append_impl_noargs (bson_append_now_utc); + bson_array_builder_append_impl_noargs(bson_append_now_utc); } bool -bson_array_builder_append_timestamp (bson_array_builder_t *bab, uint32_t timestamp, uint32_t increment) +bson_array_builder_append_timestamp(bson_array_builder_t *bab, uint32_t timestamp, uint32_t increment) { - bson_array_builder_append_impl (bson_append_timestamp, timestamp, increment); + bson_array_builder_append_impl(bson_append_timestamp, timestamp, increment); } bool -bson_array_builder_append_undefined (bson_array_builder_t *bab) +bson_array_builder_append_undefined(bson_array_builder_t *bab) { - bson_array_builder_append_impl_noargs (bson_append_undefined); + bson_array_builder_append_impl_noargs(bson_append_undefined); } bool -bson_array_builder_append_array_builder_begin (bson_array_builder_t *bab, bson_array_builder_t **child) +bson_array_builder_append_array_builder_begin(bson_array_builder_t *bab, bson_array_builder_t **child) { - bson_array_builder_append_impl (bson_append_array_builder_begin, child); + bson_array_builder_append_impl(bson_append_array_builder_begin, child); } bool -bson_array_builder_append_array_builder_end (bson_array_builder_t *bab, bson_array_builder_t *child) +bson_array_builder_append_array_builder_end(bson_array_builder_t *bab, bson_array_builder_t *child) { - return bson_append_array_builder_end (&bab->bson, child); + return bson_append_array_builder_end(&bab->bson, child); } bool -bson_array_builder_build (bson_array_builder_t *bab, bson_t *out) +bson_array_builder_build(bson_array_builder_t *bab, bson_t *out) { - BSON_ASSERT_PARAM (bab); - BSON_ASSERT_PARAM (out); - if (!bson_steal (out, &bab->bson)) { + BSON_ASSERT_PARAM(bab); + BSON_ASSERT_PARAM(out); + if (!bson_steal(out, &bab->bson)) { return false; } - bson_init (&bab->bson); + bson_init(&bab->bson); bab->index = 0; return true; } void -bson_array_builder_destroy (bson_array_builder_t *bab) +bson_array_builder_destroy(bson_array_builder_t *bab) { if (!bab) { return; } - bson_destroy (&bab->bson); - bson_free (bab); + bson_destroy(&bab->bson); + bson_free(bab); } bool -bson_append_array_builder_begin (bson_t *bson, const char *key, int key_length, bson_array_builder_t **child) +bson_append_array_builder_begin(bson_t *bson, const char *key, int key_length, bson_array_builder_t **child) { - BSON_ASSERT_PARAM (bson); - BSON_ASSERT_PARAM (key); - BSON_ASSERT_PARAM (child); - *child = bson_array_builder_new (); - return bson_append_array_begin (bson, key, key_length, &(*child)->bson); + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(key); + BSON_ASSERT_PARAM(child); + *child = bson_array_builder_new(); + bool ok = bson_append_array_unsafe_begin(bson, key, key_length, &(*child)->bson); + if (!ok) { + bson_array_builder_destroy(*child); + *child = NULL; + } + return ok; } bool -bson_append_array_builder_end (bson_t *bson, bson_array_builder_t *child) +bson_append_array_builder_end(bson_t *bson, bson_array_builder_t *child) { - bool ok = bson_append_array_end (bson, &child->bson); - bson_array_builder_destroy (child); + bool ok = bson_append_array_end(bson, &child->bson); + bson_array_builder_destroy(child); return ok; } diff --git a/bsonjs/bson/bson.h b/bsonjs/bson/bson.h index f86967b..924ca69 100644 --- a/bsonjs/bson/bson.h +++ b/bsonjs/bson/bson.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,35 +20,33 @@ #define BSON_INSIDE -#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #undef BSON_INSIDE @@ -63,7 +61,7 @@ BSON_BEGIN_DECLS * a 5 byte document which contains the length (4 bytes) and a single NUL * byte indicating end of fields. */ -#define bson_empty(b) (((b)->len == 5) || !bson_get_data ((b))[4]) +#define bson_empty(b) (((b)->len == 5) || !bson_get_data((b))[4]) /** @@ -71,7 +69,7 @@ BSON_BEGIN_DECLS * * Like bson_empty() but treats NULL the same as an empty bson_t document. */ -#define bson_empty0(b) (!(b) || bson_empty (b)) +#define bson_empty0(b) (!(b) || bson_empty(b)) /** @@ -83,12 +81,12 @@ BSON_BEGIN_DECLS * bson_clear (&doc); * BSON_ASSERT (doc == NULL); */ -#define bson_clear(bptr) \ - do { \ - if (*(bptr)) { \ - bson_destroy (*(bptr)); \ - *(bptr) = NULL; \ - } \ +#define bson_clear(bptr) \ + do { \ + if (*(bptr)) { \ + bson_destroy(*(bptr)); \ + *(bptr) = NULL; \ + } \ } while (0) @@ -97,61 +95,61 @@ BSON_BEGIN_DECLS * * The maximum size in bytes of a BSON document. */ -#define BSON_MAX_SIZE ((size_t) ((1U << 31) - 1)) +#define BSON_MAX_SIZE ((size_t)((1U << 31) - 1)) -#define BSON_APPEND_ARRAY(b, key, val) bson_append_array (b, key, (int) strlen (key), val) +#define BSON_APPEND_ARRAY(b, key, val) bson_append_array(b, key, (int)strlen(key), val) -#define BSON_APPEND_ARRAY_BEGIN(b, key, child) bson_append_array_begin (b, key, (int) strlen (key), child) +#define BSON_APPEND_ARRAY_BEGIN(b, key, child) bson_append_array_begin(b, key, (int)strlen(key), child) -#define BSON_APPEND_BINARY(b, key, subtype, val, len) bson_append_binary (b, key, (int) strlen (key), subtype, val, len) +#define BSON_APPEND_BINARY(b, key, subtype, val, len) bson_append_binary(b, key, (int)strlen(key), subtype, val, len) -#define BSON_APPEND_BOOL(b, key, val) bson_append_bool (b, key, (int) strlen (key), val) +#define BSON_APPEND_BOOL(b, key, val) bson_append_bool(b, key, (int)strlen(key), val) -#define BSON_APPEND_CODE(b, key, val) bson_append_code (b, key, (int) strlen (key), val) +#define BSON_APPEND_CODE(b, key, val) bson_append_code(b, key, (int)strlen(key), val) #define BSON_APPEND_CODE_WITH_SCOPE(b, key, val, scope) \ - bson_append_code_with_scope (b, key, (int) strlen (key), val, scope) + bson_append_code_with_scope(b, key, (int)strlen(key), val, scope) -#define BSON_APPEND_DBPOINTER(b, key, coll, oid) bson_append_dbpointer (b, key, (int) strlen (key), coll, oid) +#define BSON_APPEND_DBPOINTER(b, key, coll, oid) bson_append_dbpointer(b, key, (int)strlen(key), coll, oid) -#define BSON_APPEND_DOCUMENT_BEGIN(b, key, child) bson_append_document_begin (b, key, (int) strlen (key), child) +#define BSON_APPEND_DOCUMENT_BEGIN(b, key, child) bson_append_document_begin(b, key, (int)strlen(key), child) -#define BSON_APPEND_DOUBLE(b, key, val) bson_append_double (b, key, (int) strlen (key), val) +#define BSON_APPEND_DOUBLE(b, key, val) bson_append_double(b, key, (int)strlen(key), val) -#define BSON_APPEND_DOCUMENT(b, key, val) bson_append_document (b, key, (int) strlen (key), val) +#define BSON_APPEND_DOCUMENT(b, key, val) bson_append_document(b, key, (int)strlen(key), val) -#define BSON_APPEND_INT32(b, key, val) bson_append_int32 (b, key, (int) strlen (key), val) +#define BSON_APPEND_INT32(b, key, val) bson_append_int32(b, key, (int)strlen(key), val) -#define BSON_APPEND_INT64(b, key, val) bson_append_int64 (b, key, (int) strlen (key), val) +#define BSON_APPEND_INT64(b, key, val) bson_append_int64(b, key, (int)strlen(key), val) -#define BSON_APPEND_MINKEY(b, key) bson_append_minkey (b, key, (int) strlen (key)) +#define BSON_APPEND_MINKEY(b, key) bson_append_minkey(b, key, (int)strlen(key)) -#define BSON_APPEND_DECIMAL128(b, key, val) bson_append_decimal128 (b, key, (int) strlen (key), val) +#define BSON_APPEND_DECIMAL128(b, key, val) bson_append_decimal128(b, key, (int)strlen(key), val) -#define BSON_APPEND_MAXKEY(b, key) bson_append_maxkey (b, key, (int) strlen (key)) +#define BSON_APPEND_MAXKEY(b, key) bson_append_maxkey(b, key, (int)strlen(key)) -#define BSON_APPEND_NULL(b, key) bson_append_null (b, key, (int) strlen (key)) +#define BSON_APPEND_NULL(b, key) bson_append_null(b, key, (int)strlen(key)) -#define BSON_APPEND_OID(b, key, val) bson_append_oid (b, key, (int) strlen (key), val) +#define BSON_APPEND_OID(b, key, val) bson_append_oid(b, key, (int)strlen(key), val) -#define BSON_APPEND_REGEX(b, key, val, opt) bson_append_regex (b, key, (int) strlen (key), val, opt) +#define BSON_APPEND_REGEX(b, key, val, opt) bson_append_regex(b, key, (int)strlen(key), val, opt) -#define BSON_APPEND_UTF8(b, key, val) bson_append_utf8 (b, key, (int) strlen (key), val, (int) strlen (val)) +#define BSON_APPEND_UTF8(b, key, val) bson_append_utf8(b, key, (int)strlen(key), val, (int)strlen(val)) -#define BSON_APPEND_SYMBOL(b, key, val) bson_append_symbol (b, key, (int) strlen (key), val, (int) strlen (val)) +#define BSON_APPEND_SYMBOL(b, key, val) bson_append_symbol(b, key, (int)strlen(key), val, (int)strlen(val)) -#define BSON_APPEND_TIME_T(b, key, val) bson_append_time_t (b, key, (int) strlen (key), val) +#define BSON_APPEND_TIME_T(b, key, val) bson_append_time_t(b, key, (int)strlen(key), val) -#define BSON_APPEND_TIMEVAL(b, key, val) bson_append_timeval (b, key, (int) strlen (key), val) +#define BSON_APPEND_TIMEVAL(b, key, val) bson_append_timeval(b, key, (int)strlen(key), val) -#define BSON_APPEND_DATE_TIME(b, key, val) bson_append_date_time (b, key, (int) strlen (key), val) +#define BSON_APPEND_DATE_TIME(b, key, val) bson_append_date_time(b, key, (int)strlen(key), val) -#define BSON_APPEND_TIMESTAMP(b, key, val, inc) bson_append_timestamp (b, key, (int) strlen (key), val, inc) +#define BSON_APPEND_TIMESTAMP(b, key, val, inc) bson_append_timestamp(b, key, (int)strlen(key), val, inc) -#define BSON_APPEND_UNDEFINED(b, key) bson_append_undefined (b, key, (int) strlen (key)) +#define BSON_APPEND_UNDEFINED(b, key) bson_append_undefined(b, key, (int)strlen(key)) -#define BSON_APPEND_VALUE(b, key, val) bson_append_value (b, key, (int) strlen (key), (val)) +#define BSON_APPEND_VALUE(b, key, val) bson_append_value(b, key, (int)strlen(key), (val)) /** @@ -163,16 +161,15 @@ BSON_BEGIN_DECLS * * Returns: A newly allocated bson_t that should be freed with bson_destroy(). */ -BSON_EXPORT (bson_t *) -bson_new (void); +BSON_EXPORT(bson_t *) bson_new(void); -BSON_EXPORT (bson_t *) -bson_new_from_json (const uint8_t *data, ssize_t len, bson_error_t *error); +BSON_EXPORT(bson_t *) +bson_new_from_json(const uint8_t *data, ssize_t len, bson_error_t *error); -BSON_EXPORT (bool) -bson_init_from_json (bson_t *bson, const char *data, ssize_t len, bson_error_t *error); +BSON_EXPORT(bool) +bson_init_from_json(bson_t *bson, const char *data, ssize_t len, bson_error_t *error); /** @@ -187,8 +184,8 @@ bson_init_from_json (bson_t *bson, const char *data, ssize_t len, bson_error_t * * * Returns: true if initialized successfully; otherwise false. */ -BSON_EXPORT (bool) -bson_init_static (bson_t *b, const uint8_t *data, size_t length); +BSON_EXPORT(bool) +bson_init_static(bson_t *b, const uint8_t *data, size_t length); /** @@ -204,8 +201,8 @@ bson_init_static (bson_t *b, const uint8_t *data, size_t length); * You must call bson_destroy() with @b to release resources when you are done * using @b. */ -BSON_EXPORT (void) -bson_init (bson_t *b); +BSON_EXPORT(void) +bson_init(bson_t *b); /** @@ -217,8 +214,8 @@ bson_init (bson_t *b); * This is useful in cases where you want to reduce malloc overhead while * building many documents. */ -BSON_EXPORT (void) -bson_reinit (bson_t *b); +BSON_EXPORT(void) +bson_reinit(bson_t *b); /** @@ -233,8 +230,8 @@ bson_reinit (bson_t *b); * If the first four bytes (little-endian) of data do not match @length, * then NULL will be returned. */ -BSON_EXPORT (bson_t *) -bson_new_from_data (const uint8_t *data, size_t length); +BSON_EXPORT(bson_t *) +bson_new_from_data(const uint8_t *data, size_t length); /** @@ -250,8 +247,8 @@ bson_new_from_data (const uint8_t *data, size_t length); * Returns: A newly allocated bson_t that should be freed with bson_destroy(). * The underlying buffer will be used and not be freed in destroy. */ -BSON_EXPORT (bson_t *) -bson_new_from_buffer (uint8_t **buf, size_t *buf_len, bson_realloc_func realloc_func, void *realloc_func_ctx); +BSON_EXPORT(bson_t *) +bson_new_from_buffer(uint8_t **buf, size_t *buf_len, bson_realloc_func realloc_func, void *realloc_func_ctx); /** @@ -263,8 +260,8 @@ bson_new_from_buffer (uint8_t **buf, size_t *buf_len, bson_realloc_func realloc_ * * Returns: A newly allocated bson_t that should be freed with bson_destroy(). */ -BSON_EXPORT (bson_t *) -bson_sized_new (size_t size); +BSON_EXPORT(bson_t *) +bson_sized_new(size_t size); /** @@ -276,8 +273,8 @@ bson_sized_new (size_t size); * * Returns: A newly allocated bson_t that should be free'd with bson_destroy() */ -BSON_EXPORT (bson_t *) -bson_copy (const bson_t *bson); +BSON_EXPORT(bson_t *) +bson_copy(const bson_t *bson); /** @@ -287,24 +284,8 @@ bson_copy (const bson_t *bson); * * Initializes @dst and copies the content from @src into @dst. */ -BSON_EXPORT (void) -bson_copy_to (const bson_t *src, bson_t *dst); - - -/** - * bson_copy_to_excluding: - * @src: A bson_t. - * @dst: A bson_t to initialize and copy into. - * @first_exclude: First field name to exclude. - * - * Copies @src into @dst excluding any field that is provided. - * This is handy for situations when you need to remove one or - * more fields in a bson_t. Note that bson_init() will be called - * on dst. - */ -BSON_EXPORT (void) -bson_copy_to_excluding (const bson_t *src, bson_t *dst, const char *first_exclude, ...) BSON_GNUC_NULL_TERMINATED - BSON_GNUC_DEPRECATED_FOR (bson_copy_to_excluding_noinit); +BSON_EXPORT(void) +bson_copy_to(const bson_t *src, bson_t *dst); /** * bson_copy_to_excluding_noinit: @@ -312,16 +293,13 @@ bson_copy_to_excluding (const bson_t *src, bson_t *dst, const char *first_exclud * @dst: A bson_t to initialize and copy into. * @first_exclude: First field name to exclude. * - * The same as bson_copy_to_excluding, but does not call bson_init() - * on the dst. This version should be preferred in new code, but the - * old function is left for backwards compatibility. + * Does not call bson_init() on the dst. */ -BSON_EXPORT (void) -bson_copy_to_excluding_noinit (const bson_t *src, bson_t *dst, const char *first_exclude, ...) - BSON_GNUC_NULL_TERMINATED; +BSON_EXPORT(void) +bson_copy_to_excluding_noinit(const bson_t *src, bson_t *dst, const char *first_exclude, ...) BSON_GNUC_NULL_TERMINATED; -BSON_EXPORT (void) -bson_copy_to_excluding_noinit_va (const bson_t *src, bson_t *dst, const char *first_exclude, va_list args); +BSON_EXPORT(void) +bson_copy_to_excluding_noinit_va(const bson_t *src, bson_t *dst, const char *first_exclude, va_list args); /** @@ -330,14 +308,14 @@ bson_copy_to_excluding_noinit_va (const bson_t *src, bson_t *dst, const char *fi * * Frees the resources associated with @bson. */ -BSON_EXPORT (void) -bson_destroy (bson_t *bson); +BSON_EXPORT(void) +bson_destroy(bson_t *bson); -BSON_EXPORT (uint8_t *) -bson_reserve_buffer (bson_t *bson, uint32_t size); +BSON_EXPORT(uint8_t *) +bson_reserve_buffer(bson_t *bson, uint32_t total_size); -BSON_EXPORT (bool) -bson_steal (bson_t *dst, bson_t *src); +BSON_EXPORT(bool) +bson_steal(bson_t *dst, bson_t *src); /** @@ -359,8 +337,8 @@ bson_steal (bson_t *dst, bson_t *src); * Returns: a buffer owned by the caller if @steal is true. Otherwise NULL. * If there was an error, NULL is returned. */ -BSON_EXPORT (uint8_t *) -bson_destroy_with_steal (bson_t *bson, bool steal, uint32_t *length); +BSON_EXPORT(uint8_t *) +bson_destroy_with_steal(bson_t *bson, bool steal, uint32_t *length); /** @@ -371,8 +349,8 @@ bson_destroy_with_steal (bson_t *bson, bool steal, uint32_t *length); * * Returns: A buffer that should not be modified or freed. */ -BSON_EXPORT (const uint8_t *) -bson_get_data (const bson_t *bson); +BSON_EXPORT(const uint8_t *) +bson_get_data(const bson_t *bson); /** @@ -381,8 +359,8 @@ bson_get_data (const bson_t *bson); * * Counts the number of elements found in @bson. */ -BSON_EXPORT (uint32_t) -bson_count_keys (const bson_t *bson); +BSON_EXPORT(uint32_t) +bson_count_keys(const bson_t *bson); /** @@ -396,8 +374,8 @@ bson_count_keys (const bson_t *bson); * * Returns: true if @key exists in @bson; otherwise false. */ -BSON_EXPORT (bool) -bson_has_field (const bson_t *bson, const char *key); +BSON_EXPORT(bool) +bson_has_field(const bson_t *bson, const char *key); /** @@ -410,8 +388,8 @@ bson_has_field (const bson_t *bson, const char *key); * * Returns: Less than zero, zero, or greater than zero. */ -BSON_EXPORT (int) -bson_compare (const bson_t *bson, const bson_t *other); +BSON_EXPORT(int) +bson_compare(const bson_t *bson, const bson_t *other); /* * bson_equal: @@ -422,8 +400,8 @@ bson_compare (const bson_t *bson, const bson_t *other); * * Returns: true if equal; otherwise false. */ -BSON_EXPORT (bool) -bson_equal (const bson_t *bson, const bson_t *other); +BSON_EXPORT(bool) +bson_equal(const bson_t *bson, const bson_t *other); /** @@ -436,8 +414,8 @@ bson_equal (const bson_t *bson, const bson_t *other); * * Returns: true if @bson is valid; otherwise false and @offset is set. */ -BSON_EXPORT (bool) -bson_validate (const bson_t *bson, bson_validate_flags_t flags, size_t *offset); +BSON_EXPORT(bool) +bson_validate(const bson_t *bson, bson_validate_flags_t flags, size_t *offset); /** @@ -450,8 +428,27 @@ bson_validate (const bson_t *bson, bson_validate_flags_t flags, size_t *offset); * * Returns: true if @bson is valid; otherwise false and @error is filled out. */ -BSON_EXPORT (bool) -bson_validate_with_error (const bson_t *bson, bson_validate_flags_t flags, bson_error_t *error); +BSON_EXPORT(bool) +bson_validate_with_error(const bson_t *bson, bson_validate_flags_t flags, bson_error_t *error); + + +/** + * bson_validate_with_error_and_offset: + * @bson: A bson_t. + * @offset: A location for the error offset. + * @error: A location for the error info. + * + * Validates a BSON document by walking through the document and inspecting + * the fields for valid content. + * + * Returns: true if @bson is valid; otherwise false, @offset is set + * and @error is filled out. + */ +BSON_EXPORT(bool) +bson_validate_with_error_and_offset(const bson_t *bson, + bson_validate_flags_t flags, + size_t *offset, + bson_error_t *error); /** @@ -473,8 +470,8 @@ bson_validate_with_error (const bson_t *bson, bson_validate_flags_t flags, bson_ * * Returns: A newly allocated string that should be freed with bson_free(). */ -BSON_EXPORT (char *) -bson_as_json_with_opts (const bson_t *bson, size_t *length, const bson_json_opts_t *opts); +BSON_EXPORT(char *) +bson_as_json_with_opts(const bson_t *bson, size_t *length, const bson_json_opts_t *opts); /** @@ -495,12 +492,12 @@ bson_as_json_with_opts (const bson_t *bson, size_t *length, const bson_json_opts * * Returns: A newly allocated string that should be freed with bson_free(). */ -BSON_EXPORT (char *) -bson_as_canonical_extended_json (const bson_t *bson, size_t *length); +BSON_EXPORT(char *) +bson_as_canonical_extended_json(const bson_t *bson, size_t *length); /** - * bson_as_json: + * bson_as_legacy_extended_json: * @bson: A bson_t. * @length: A location for the string length, or NULL. * @@ -512,8 +509,9 @@ bson_as_canonical_extended_json (const bson_t *bson, size_t *length); * * Returns: A newly allocated string that should be freed with bson_free(). */ -BSON_EXPORT (char *) -bson_as_json (const bson_t *bson, size_t *length); + +BSON_EXPORT(char *) +bson_as_legacy_extended_json(const bson_t *bson, size_t *length); /** @@ -534,22 +532,23 @@ bson_as_json (const bson_t *bson, size_t *length); * * Returns: A newly allocated string that should be freed with bson_free(). */ -BSON_EXPORT (char *) -bson_as_relaxed_extended_json (const bson_t *bson, size_t *length); +BSON_EXPORT(char *) +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_legacy_extended_json() but for outermost arrays. */ +BSON_EXPORT(char *) +bson_array_as_legacy_extended_json(const bson_t *bson, size_t *length); /* like bson_as_relaxed_extended_json() but for outermost arrays. */ -BSON_EXPORT (char *) -bson_array_as_relaxed_extended_json (const bson_t *bson, size_t *length); +BSON_EXPORT(char *) +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(char *) +bson_array_as_canonical_extended_json(const bson_t *bson, size_t *length); // bson_array_builder_t defines an API for building arrays. // BSON arrays require sequential numeric keys "0", "1", "2", ... @@ -559,23 +558,23 @@ typedef struct _bson_array_builder_t bson_array_builder_t; // `[1,2,3]`. // To append an array field to a document (Example: `{ "field": [1,2,3] }`), use // `bson_append_array_builder_begin`. -BSON_EXPORT (bson_array_builder_t *) bson_array_builder_new (void); +BSON_EXPORT(bson_array_builder_t *) bson_array_builder_new(void); // bson_array_builder_build initializes and moves BSON data to `out`. // `bab` may be reused and will start appending a new array at index "0". -BSON_EXPORT (bool) -bson_array_builder_build (bson_array_builder_t *bab, bson_t *out); +BSON_EXPORT(bool) +bson_array_builder_build(bson_array_builder_t *bab, bson_t *out); -BSON_EXPORT (void) -bson_array_builder_destroy (bson_array_builder_t *bab); +BSON_EXPORT(void) +bson_array_builder_destroy(bson_array_builder_t *bab); -BSON_EXPORT (bool) -bson_append_value (bson_t *bson, const char *key, int key_length, const bson_value_t *value); +BSON_EXPORT(bool) +bson_append_value(bson_t *bson, const char *key, int key_length, const bson_value_t *value); -#define BSON_APPEND_VALUE(b, key, val) bson_append_value (b, key, (int) strlen (key), (val)) +#define BSON_APPEND_VALUE(b, key, val) bson_append_value(b, key, (int)strlen(key), (val)) -BSON_EXPORT (bool) -bson_array_builder_append_value (bson_array_builder_t *bab, const bson_value_t *value); +BSON_EXPORT(bool) +bson_array_builder_append_value(bson_array_builder_t *bab, const bson_value_t *value); /** * bson_append_array: @@ -589,19 +588,39 @@ bson_array_builder_append_value (bson_array_builder_t *bab, const bson_value_t * * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_array (bson_t *bson, const char *key, int key_length, const bson_t *array); +BSON_EXPORT(bool) +bson_append_array(bson_t *bson, const char *key, int key_length, const bson_t *array); + +#define BSON_APPEND_ARRAY(b, key, val) bson_append_array(b, key, (int)strlen(key), val) + +BSON_EXPORT(bool) +bson_array_builder_append_array(bson_array_builder_t *bab, const bson_t *array); + +/** + * bson_append_array_from_vector: + * @bson: A bson_t that will be modified. + * @key: The key for the field. + * @iter: A bson_iter_t pointing to any supported vector in another bson_t. + * + * If @iter points to a supported vector type, converts the vector to a BSON array appended to @bson. + * + * Returns: true if successful; false if append would overflow max size or @iter does not point to a vector in a + * supported format. + */ +BSON_EXPORT(bool) +bson_append_array_from_vector(bson_t *bson, const char *key, int key_length, const bson_iter_t *iter); -#define BSON_APPEND_ARRAY(b, key, val) bson_append_array (b, key, (int) strlen (key), val) +#define BSON_APPEND_ARRAY_FROM_VECTOR(b, key, iter) bson_append_array_from_vector(b, key, (int)strlen(key), iter) -BSON_EXPORT (bool) -bson_array_builder_append_array (bson_array_builder_t *bab, const bson_t *array); +BSON_EXPORT(bool) +bson_array_builder_append_array_from_vector(bson_array_builder_t *bab, const bson_iter_t *iter); /** * bson_append_binary: - * @bson: A bson_t to append. + * @bson: A bson_t. * @key: The key for the field. - * @subtype: The bson_subtype_t of the binary. + * @key_length: Optional length of 'key' in bytes, or -1 to use strlen(key). + * @subtype: The bson_subtype_t of the binary item. * @binary: The binary buffer to append. * @length: The length of @binary. * @@ -609,17 +628,41 @@ bson_array_builder_append_array (bson_array_builder_t *bab, const bson_t *array) * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_binary ( +BSON_EXPORT(bool) +bson_append_binary( bson_t *bson, const char *key, int key_length, bson_subtype_t subtype, const uint8_t *binary, uint32_t length); -#define BSON_APPEND_BINARY(b, key, subtype, val, len) bson_append_binary (b, key, (int) strlen (key), subtype, val, len) +#define BSON_APPEND_BINARY(b, key, subtype, val, len) bson_append_binary(b, key, (int)strlen(key), subtype, val, len) + +BSON_EXPORT(bool) +bson_array_builder_append_binary(bson_array_builder_t *bab, + bson_subtype_t subtype, + const uint8_t *binary, + uint32_t length); + +/** + * bson_append_binary_uninit: + * @bson: A bson_t. + * @key: The key for the field. + * @key_length: Optional length of 'key' in bytes, or -1 to use strlen(key). + * @binary: Output parameter, pointer for the binary data within bson_t to be written. + * @length: Length of the binary field to allocate, in bytes. + * + * Returns: true if successful; false if append would overflow max size. + */ + +BSON_EXPORT(bool) +bson_append_binary_uninit( + bson_t *bson, const char *key, int key_length, bson_subtype_t subtype, uint8_t **binary, uint32_t length); -BSON_EXPORT (bool) -bson_array_builder_append_binary (bson_array_builder_t *bab, - bson_subtype_t subtype, - const uint8_t *binary, - uint32_t length); +#define BSON_APPEND_BINARY_UNINIT(b, key, subtype, val, len) \ + bson_append_binary_uninit(b, key, (int)strlen(key), subtype, val, len) + +BSON_EXPORT(bool) +bson_array_builder_append_binary_uninit(bson_array_builder_t *bab, + bson_subtype_t subtype, + uint8_t **binary, + uint32_t length); /** * bson_append_bool: @@ -631,13 +674,13 @@ bson_array_builder_append_binary (bson_array_builder_t *bab, * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_bool (bson_t *bson, const char *key, int key_length, bool value); +BSON_EXPORT(bool) +bson_append_bool(bson_t *bson, const char *key, int key_length, bool value); -#define BSON_APPEND_BOOL(b, key, val) bson_append_bool (b, key, (int) strlen (key), val) +#define BSON_APPEND_BOOL(b, key, val) bson_append_bool(b, key, (int)strlen(key), val) -BSON_EXPORT (bool) -bson_array_builder_append_bool (bson_array_builder_t *bab, bool value); +BSON_EXPORT(bool) +bson_array_builder_append_bool(bson_array_builder_t *bab, bool value); /** * bson_append_code: @@ -650,13 +693,13 @@ bson_array_builder_append_bool (bson_array_builder_t *bab, bool value); * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_code (bson_t *bson, const char *key, int key_length, const char *javascript); +BSON_EXPORT(bool) +bson_append_code(bson_t *bson, const char *key, int key_length, const char *javascript); -#define BSON_APPEND_CODE(b, key, val) bson_append_code (b, key, (int) strlen (key), val) +#define BSON_APPEND_CODE(b, key, val) bson_append_code(b, key, (int)strlen(key), val) -BSON_EXPORT (bool) -bson_array_builder_append_code (bson_array_builder_t *bab, const char *javascript); +BSON_EXPORT(bool) +bson_array_builder_append_code(bson_array_builder_t *bab, const char *javascript); /** * bson_append_code_with_scope: @@ -670,15 +713,14 @@ bson_array_builder_append_code (bson_array_builder_t *bab, const char *javascrip * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_code_with_scope ( - bson_t *bson, const char *key, int key_length, const char *javascript, const bson_t *scope); +BSON_EXPORT(bool) +bson_append_code_with_scope(bson_t *bson, const char *key, int key_length, const char *javascript, const bson_t *scope); #define BSON_APPEND_CODE_WITH_SCOPE(b, key, val, scope) \ - bson_append_code_with_scope (b, key, (int) strlen (key), val, scope) + bson_append_code_with_scope(b, key, (int)strlen(key), val, scope) -BSON_EXPORT (bool) -bson_array_builder_append_code_with_scope (bson_array_builder_t *bab, const char *javascript, const bson_t *scope); +BSON_EXPORT(bool) +bson_array_builder_append_code_with_scope(bson_array_builder_t *bab, const char *javascript, const bson_t *scope); /** * bson_append_dbpointer: @@ -692,13 +734,13 @@ bson_array_builder_append_code_with_scope (bson_array_builder_t *bab, const char * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_dbpointer (bson_t *bson, const char *key, int key_length, const char *collection, const bson_oid_t *oid); +BSON_EXPORT(bool) +bson_append_dbpointer(bson_t *bson, const char *key, int key_length, const char *collection, const bson_oid_t *oid); -#define BSON_APPEND_DBPOINTER(b, key, coll, oid) bson_append_dbpointer (b, key, (int) strlen (key), coll, oid) +#define BSON_APPEND_DBPOINTER(b, key, coll, oid) bson_append_dbpointer(b, key, (int)strlen(key), coll, oid) -BSON_EXPORT (bool) -bson_array_builder_append_dbpointer (bson_array_builder_t *bab, const char *collection, const bson_oid_t *oid); +BSON_EXPORT(bool) +bson_array_builder_append_dbpointer(bson_array_builder_t *bab, const char *collection, const bson_oid_t *oid); /** * bson_append_double: @@ -709,13 +751,13 @@ bson_array_builder_append_dbpointer (bson_array_builder_t *bab, const char *coll * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_double (bson_t *bson, const char *key, int key_length, double value); +BSON_EXPORT(bool) +bson_append_double(bson_t *bson, const char *key, int key_length, double value); -#define BSON_APPEND_DOUBLE(b, key, val) bson_append_double (b, key, (int) strlen (key), val) +#define BSON_APPEND_DOUBLE(b, key, val) bson_append_double(b, key, (int)strlen(key), val) -BSON_EXPORT (bool) -bson_array_builder_append_double (bson_array_builder_t *bab, double value); +BSON_EXPORT(bool) +bson_array_builder_append_double(bson_array_builder_t *bab, double value); /** * bson_append_document: @@ -728,13 +770,13 @@ bson_array_builder_append_double (bson_array_builder_t *bab, double value); * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_document (bson_t *bson, const char *key, int key_length, const bson_t *value); +BSON_EXPORT(bool) +bson_append_document(bson_t *bson, const char *key, int key_length, const bson_t *value); -#define BSON_APPEND_DOCUMENT(b, key, val) bson_append_document (b, key, (int) strlen (key), val) +#define BSON_APPEND_DOCUMENT(b, key, val) bson_append_document(b, key, (int)strlen(key), val) -BSON_EXPORT (bool) -bson_array_builder_append_document (bson_array_builder_t *bab, const bson_t *value); +BSON_EXPORT(bool) +bson_array_builder_append_document(bson_array_builder_t *bab, const bson_t *value); /** * bson_append_document_begin: @@ -753,13 +795,13 @@ bson_array_builder_append_document (bson_array_builder_t *bab, const bson_t *val * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_document_begin (bson_t *bson, const char *key, int key_length, bson_t *child); +BSON_EXPORT(bool) +bson_append_document_begin(bson_t *bson, const char *key, int key_length, bson_t *child); -#define BSON_APPEND_DOCUMENT_BEGIN(b, key, child) bson_append_document_begin (b, key, (int) strlen (key), child) +#define BSON_APPEND_DOCUMENT_BEGIN(b, key, child) bson_append_document_begin(b, key, (int)strlen(key), child) -BSON_EXPORT (bool) -bson_array_builder_append_document_begin (bson_array_builder_t *bab, bson_t *child); +BSON_EXPORT(bool) +bson_array_builder_append_document_begin(bson_array_builder_t *bab, bson_t *child); /** * bson_append_document_end: @@ -771,11 +813,11 @@ bson_array_builder_append_document_begin (bson_array_builder_t *bab, bson_t *chi * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_document_end (bson_t *bson, bson_t *child); +BSON_EXPORT(bool) +bson_append_document_end(bson_t *bson, bson_t *child); -BSON_EXPORT (bool) -bson_array_builder_append_document_end (bson_array_builder_t *bab, bson_t *child); +BSON_EXPORT(bool) +bson_array_builder_append_document_end(bson_array_builder_t *bab, bson_t *child); /** @@ -785,6 +827,7 @@ bson_array_builder_append_document_end (bson_array_builder_t *bab, bson_t *child * @key_length: The length of @key in bytes not including NUL or -1 * if @key_length is NUL terminated. * @child: A location to an uninitialized bson_t. + * @deprecated Use bson_append_array_builder_begin or bson_append_array_unsafe_begin instead. * * Appends a new field named @key to @bson. The field is, however, * incomplete. @child will be initialized so that you may add fields to the @@ -798,10 +841,16 @@ bson_array_builder_append_document_end (bson_array_builder_t *bab, bson_t *child * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_array_begin (bson_t *bson, const char *key, int key_length, bson_t *child); +BSON_GNUC_DEPRECATED_FOR(bson_append_array_builder_begin or bson_append_array_unsafe_begin) +BSON_EXPORT(bool) +bson_append_array_begin(bson_t *bson, const char *key, int key_length, bson_t *child); + +#define BSON_APPEND_ARRAY_BEGIN(b, key, child) bson_append_array_begin(b, key, (int)strlen(key), child) + +BSON_EXPORT(bool) +bson_append_array_unsafe_begin(bson_t *bson, const char *key, int key_length, bson_t *child); -#define BSON_APPEND_ARRAY_BEGIN(b, key, child) bson_append_array_begin (b, key, (int) strlen (key), child) +#define BSON_APPEND_ARRAY_UNSAFE_BEGIN(b, key, child) bson_append_array_unsafe_begin(b, key, (int)strlen(key), child) /** * bson_append_array_end: @@ -813,8 +862,8 @@ bson_append_array_begin (bson_t *bson, const char *key, int key_length, bson_t * * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_array_end (bson_t *bson, bson_t *child); +BSON_EXPORT(bool) +bson_append_array_end(bson_t *bson, bson_t *child); /** @@ -827,13 +876,13 @@ bson_append_array_end (bson_t *bson, bson_t *child); * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_int32 (bson_t *bson, const char *key, int key_length, int32_t value); +BSON_EXPORT(bool) +bson_append_int32(bson_t *bson, const char *key, int key_length, int32_t value); -#define BSON_APPEND_INT32(b, key, val) bson_append_int32 (b, key, (int) strlen (key), val) +#define BSON_APPEND_INT32(b, key, val) bson_append_int32(b, key, (int)strlen(key), val) -BSON_EXPORT (bool) -bson_array_builder_append_int32 (bson_array_builder_t *bab, int32_t value); +BSON_EXPORT(bool) +bson_array_builder_append_int32(bson_array_builder_t *bab, int32_t value); /** * bson_append_int64: @@ -845,13 +894,13 @@ bson_array_builder_append_int32 (bson_array_builder_t *bab, int32_t value); * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_int64 (bson_t *bson, const char *key, int key_length, int64_t value); +BSON_EXPORT(bool) +bson_append_int64(bson_t *bson, const char *key, int key_length, int64_t value); -#define BSON_APPEND_INT64(b, key, val) bson_append_int64 (b, key, (int) strlen (key), val) +#define BSON_APPEND_INT64(b, key, val) bson_append_int64(b, key, (int)strlen(key), val) -BSON_EXPORT (bool) -bson_array_builder_append_int64 (bson_array_builder_t *bab, int64_t value); +BSON_EXPORT(bool) +bson_array_builder_append_int64(bson_array_builder_t *bab, int64_t value); /** * bson_append_decimal128: @@ -863,13 +912,13 @@ bson_array_builder_append_int64 (bson_array_builder_t *bab, int64_t value); * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_decimal128 (bson_t *bson, const char *key, int key_length, const bson_decimal128_t *value); +BSON_EXPORT(bool) +bson_append_decimal128(bson_t *bson, const char *key, int key_length, const bson_decimal128_t *value); -#define BSON_APPEND_DECIMAL128(b, key, val) bson_append_decimal128 (b, key, (int) strlen (key), val) +#define BSON_APPEND_DECIMAL128(b, key, val) bson_append_decimal128(b, key, (int)strlen(key), val) -BSON_EXPORT (bool) -bson_array_builder_append_decimal128 (bson_array_builder_t *bab, const bson_decimal128_t *value); +BSON_EXPORT(bool) +bson_array_builder_append_decimal128(bson_array_builder_t *bab, const bson_decimal128_t *value); /** * bson_append_iter: @@ -883,13 +932,13 @@ bson_array_builder_append_decimal128 (bson_array_builder_t *bab, const bson_deci * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_iter (bson_t *bson, const char *key, int key_length, const bson_iter_t *iter); +BSON_EXPORT(bool) +bson_append_iter(bson_t *bson, const char *key, int key_length, const bson_iter_t *iter); -#define BSON_APPEND_ITER(b, key, val) bson_append_iter (b, key, (int) strlen (key), val) +#define BSON_APPEND_ITER(b, key, val) bson_append_iter(b, key, (int)strlen(key), val) -BSON_EXPORT (bool) -bson_array_builder_append_iter (bson_array_builder_t *bab, const bson_iter_t *iter); +BSON_EXPORT(bool) +bson_array_builder_append_iter(bson_array_builder_t *bab, const bson_iter_t *iter); /** * bson_append_minkey: @@ -903,13 +952,13 @@ bson_array_builder_append_iter (bson_array_builder_t *bab, const bson_iter_t *it * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_minkey (bson_t *bson, const char *key, int key_length); +BSON_EXPORT(bool) +bson_append_minkey(bson_t *bson, const char *key, int key_length); -#define BSON_APPEND_MINKEY(b, key) bson_append_minkey (b, key, (int) strlen (key)) +#define BSON_APPEND_MINKEY(b, key) bson_append_minkey(b, key, (int)strlen(key)) -BSON_EXPORT (bool) -bson_array_builder_append_minkey (bson_array_builder_t *bab); +BSON_EXPORT(bool) +bson_array_builder_append_minkey(bson_array_builder_t *bab); /** * bson_append_maxkey: @@ -923,13 +972,13 @@ bson_array_builder_append_minkey (bson_array_builder_t *bab); * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_maxkey (bson_t *bson, const char *key, int key_length); +BSON_EXPORT(bool) +bson_append_maxkey(bson_t *bson, const char *key, int key_length); -#define BSON_APPEND_MAXKEY(b, key) bson_append_maxkey (b, key, (int) strlen (key)) +#define BSON_APPEND_MAXKEY(b, key) bson_append_maxkey(b, key, (int)strlen(key)) -BSON_EXPORT (bool) -bson_array_builder_append_maxkey (bson_array_builder_t *bab); +BSON_EXPORT(bool) +bson_array_builder_append_maxkey(bson_array_builder_t *bab); /** * bson_append_null: @@ -940,13 +989,13 @@ bson_array_builder_append_maxkey (bson_array_builder_t *bab); * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_null (bson_t *bson, const char *key, int key_length); +BSON_EXPORT(bool) +bson_append_null(bson_t *bson, const char *key, int key_length); -#define BSON_APPEND_NULL(b, key) bson_append_null (b, key, (int) strlen (key)) +#define BSON_APPEND_NULL(b, key) bson_append_null(b, key, (int)strlen(key)) -BSON_EXPORT (bool) -bson_array_builder_append_null (bson_array_builder_t *bab); +BSON_EXPORT(bool) +bson_array_builder_append_null(bson_array_builder_t *bab); /** * bson_append_oid: @@ -959,13 +1008,13 @@ bson_array_builder_append_null (bson_array_builder_t *bab); * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_oid (bson_t *bson, const char *key, int key_length, const bson_oid_t *oid); +BSON_EXPORT(bool) +bson_append_oid(bson_t *bson, const char *key, int key_length, const bson_oid_t *oid); -#define BSON_APPEND_OID(b, key, val) bson_append_oid (b, key, (int) strlen (key), val) +#define BSON_APPEND_OID(b, key, val) bson_append_oid(b, key, (int)strlen(key), val) -BSON_EXPORT (bool) -bson_array_builder_append_oid (bson_array_builder_t *bab, const bson_oid_t *oid); +BSON_EXPORT(bool) +bson_array_builder_append_oid(bson_array_builder_t *bab, const bson_oid_t *oid); /** * bson_append_regex: @@ -990,13 +1039,13 @@ bson_array_builder_append_oid (bson_array_builder_t *bab, const bson_oid_t *oid) * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_regex (bson_t *bson, const char *key, int key_length, const char *regex, const char *options); +BSON_EXPORT(bool) +bson_append_regex(bson_t *bson, const char *key, int key_length, const char *regex, const char *options); -#define BSON_APPEND_REGEX(b, key, val, opt) bson_append_regex (b, key, (int) strlen (key), val, opt) +#define BSON_APPEND_REGEX(b, key, val, opt) bson_append_regex(b, key, (int)strlen(key), val, opt) -BSON_EXPORT (bool) -bson_array_builder_append_regex (bson_array_builder_t *bab, const char *regex, const char *options); +BSON_EXPORT(bool) +bson_array_builder_append_regex(bson_array_builder_t *bab, const char *regex, const char *options); /** * bson_append_regex: @@ -1023,15 +1072,15 @@ bson_array_builder_append_regex (bson_array_builder_t *bab, const char *regex, c * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_regex_w_len ( +BSON_EXPORT(bool) +bson_append_regex_w_len( bson_t *bson, const char *key, int key_length, const char *regex, int regex_length, const char *options); -BSON_EXPORT (bool) -bson_array_builder_append_regex_w_len (bson_array_builder_t *bab, - const char *regex, - int regex_length, - const char *options); +BSON_EXPORT(bool) +bson_array_builder_append_regex_w_len(bson_array_builder_t *bab, + const char *regex, + int regex_length, + const char *options); /** * bson_append_utf8: @@ -1048,13 +1097,15 @@ bson_array_builder_append_regex_w_len (bson_array_builder_t *bab, * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_utf8 (bson_t *bson, const char *key, int key_length, const char *value, int length); +BSON_EXPORT(bool) +bson_append_utf8(bson_t *bson, const char *key, int key_length, const char *value, int length); -#define BSON_APPEND_UTF8(b, key, val) bson_append_utf8 (b, key, (int) strlen (key), val, (int) strlen (val)) +#define BSON_APPEND_UTF8(b, key, val) bson_append_utf8(b, key, (int)strlen(key), val, (int)strlen(val)) -BSON_EXPORT (bool) -bson_array_builder_append_utf8 (bson_array_builder_t *bab, const char *value, int length); +BSON_EXPORT(bool) +bson_array_builder_append_utf8(bson_array_builder_t *bab, const char *value, int length); + +#define BSON_APPEND_ARRAY_BUILDER_APPEND_UTF8(b, value) bson_array_builder_append_utf8(b, value, (int)strlen(value)) /** * bson_append_symbol: @@ -1070,13 +1121,15 @@ bson_array_builder_append_utf8 (bson_array_builder_t *bab, const char *value, in * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_symbol (bson_t *bson, const char *key, int key_length, const char *value, int length); +BSON_EXPORT(bool) +bson_append_symbol(bson_t *bson, const char *key, int key_length, const char *value, int length); + +#define BSON_APPEND_SYMBOL(b, key, val) bson_append_symbol(b, key, (int)strlen(key), val, (int)strlen(val)) -#define BSON_APPEND_SYMBOL(b, key, val) bson_append_symbol (b, key, (int) strlen (key), val, (int) strlen (val)) +BSON_EXPORT(bool) +bson_array_builder_append_symbol(bson_array_builder_t *bab, const char *value, int length); -BSON_EXPORT (bool) -bson_array_builder_append_symbol (bson_array_builder_t *bab, const char *value, int length); +#define BSON_APPEND_ARRAY_BUILDER_APPEND_SYMBOL(b, value) bson_array_builder_append_symbol(b, value, (int)strlen(value)) /** * bson_append_time_t: @@ -1089,13 +1142,13 @@ bson_array_builder_append_symbol (bson_array_builder_t *bab, const char *value, * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_time_t (bson_t *bson, const char *key, int key_length, time_t value); +BSON_EXPORT(bool) +bson_append_time_t(bson_t *bson, const char *key, int key_length, time_t value); -#define BSON_APPEND_TIME_T(b, key, val) bson_append_time_t (b, key, (int) strlen (key), val) +#define BSON_APPEND_TIME_T(b, key, val) bson_append_time_t(b, key, (int)strlen(key), val) -BSON_EXPORT (bool) -bson_array_builder_append_time_t (bson_array_builder_t *bab, time_t value); +BSON_EXPORT(bool) +bson_array_builder_append_time_t(bson_array_builder_t *bab, time_t value); /** * bson_append_timeval: @@ -1108,13 +1161,13 @@ bson_array_builder_append_time_t (bson_array_builder_t *bab, time_t value); * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_timeval (bson_t *bson, const char *key, int key_length, struct timeval *value); +BSON_EXPORT(bool) +bson_append_timeval(bson_t *bson, const char *key, int key_length, struct timeval *value); -#define BSON_APPEND_TIMEVAL(b, key, val) bson_append_timeval (b, key, (int) strlen (key), val) +#define BSON_APPEND_TIMEVAL(b, key, val) bson_append_timeval(b, key, (int)strlen(key), val) -BSON_EXPORT (bool) -bson_array_builder_append_timeval (bson_array_builder_t *bab, struct timeval *value); +BSON_EXPORT(bool) +bson_array_builder_append_timeval(bson_array_builder_t *bab, struct timeval *value); /** * bson_append_date_time: @@ -1127,13 +1180,13 @@ bson_array_builder_append_timeval (bson_array_builder_t *bab, struct timeval *va * * Returns: true if successful; otherwise false. */ -BSON_EXPORT (bool) -bson_append_date_time (bson_t *bson, const char *key, int key_length, int64_t value); +BSON_EXPORT(bool) +bson_append_date_time(bson_t *bson, const char *key, int key_length, int64_t value); -#define BSON_APPEND_DATE_TIME(b, key, val) bson_append_date_time (b, key, (int) strlen (key), val) +#define BSON_APPEND_DATE_TIME(b, key, val) bson_append_date_time(b, key, (int)strlen(key), val) -BSON_EXPORT (bool) -bson_array_builder_append_date_time (bson_array_builder_t *bab, int64_t value); +BSON_EXPORT(bool) +bson_array_builder_append_date_time(bson_array_builder_t *bab, int64_t value); /** * bson_append_now_utc: @@ -1146,13 +1199,13 @@ bson_array_builder_append_date_time (bson_array_builder_t *bab, int64_t value); * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_now_utc (bson_t *bson, const char *key, int key_length); +BSON_EXPORT(bool) +bson_append_now_utc(bson_t *bson, const char *key, int key_length); -#define BSON_APPEND_NOW_UTC(b, key) bson_append_now_utc (b, key, (int) strlen (key)) +#define BSON_APPEND_NOW_UTC(b, key) bson_append_now_utc(b, key, (int)strlen(key)) -BSON_EXPORT (bool) -bson_array_builder_append_now_utc (bson_array_builder_t *bab); +BSON_EXPORT(bool) +bson_array_builder_append_now_utc(bson_array_builder_t *bab); /** * bson_append_timestamp: @@ -1170,13 +1223,13 @@ bson_array_builder_append_now_utc (bson_array_builder_t *bab); * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_timestamp (bson_t *bson, const char *key, int key_length, uint32_t timestamp, uint32_t increment); +BSON_EXPORT(bool) +bson_append_timestamp(bson_t *bson, const char *key, int key_length, uint32_t timestamp, uint32_t increment); -#define BSON_APPEND_TIMESTAMP(b, key, val, inc) bson_append_timestamp (b, key, (int) strlen (key), val, inc) +#define BSON_APPEND_TIMESTAMP(b, key, val, inc) bson_append_timestamp(b, key, (int)strlen(key), val, inc) -BSON_EXPORT (bool) -bson_array_builder_append_timestamp (bson_array_builder_t *bab, uint32_t timestamp, uint32_t increment); +BSON_EXPORT(bool) +bson_array_builder_append_timestamp(bson_array_builder_t *bab, uint32_t timestamp, uint32_t increment); /** * bson_append_undefined: @@ -1189,31 +1242,30 @@ bson_array_builder_append_timestamp (bson_array_builder_t *bab, uint32_t timesta * * Returns: true if successful; false if append would overflow max size. */ -BSON_EXPORT (bool) -bson_append_undefined (bson_t *bson, const char *key, int key_length); +BSON_EXPORT(bool) +bson_append_undefined(bson_t *bson, const char *key, int key_length); -#define BSON_APPEND_UNDEFINED(b, key) bson_append_undefined (b, key, (int) strlen (key)) +#define BSON_APPEND_UNDEFINED(b, key) bson_append_undefined(b, key, (int)strlen(key)) -BSON_EXPORT (bool) -bson_array_builder_append_undefined (bson_array_builder_t *bab); +BSON_EXPORT(bool) +bson_array_builder_append_undefined(bson_array_builder_t *bab); -BSON_EXPORT (bool) -bson_concat (bson_t *dst, const bson_t *src); +BSON_EXPORT(bool) +bson_concat(bson_t *dst, const bson_t *src); -BSON_EXPORT (bool) -bson_append_array_builder_begin (bson_t *bson, const char *key, int key_length, bson_array_builder_t **child); +BSON_EXPORT(bool) +bson_append_array_builder_begin(bson_t *bson, const char *key, int key_length, bson_array_builder_t **child); -#define BSON_APPEND_ARRAY_BUILDER_BEGIN(b, key, child) \ - bson_append_array_builder_begin (b, key, (int) strlen (key), child) +#define BSON_APPEND_ARRAY_BUILDER_BEGIN(b, key, child) bson_append_array_builder_begin(b, key, (int)strlen(key), child) -BSON_EXPORT (bool) -bson_array_builder_append_array_builder_begin (bson_array_builder_t *bab, bson_array_builder_t **child); +BSON_EXPORT(bool) +bson_array_builder_append_array_builder_begin(bson_array_builder_t *bab, bson_array_builder_t **child); -BSON_EXPORT (bool) -bson_append_array_builder_end (bson_t *bson, bson_array_builder_t *child); +BSON_EXPORT(bool) +bson_append_array_builder_end(bson_t *bson, bson_array_builder_t *child); -BSON_EXPORT (bool) -bson_array_builder_append_array_builder_end (bson_array_builder_t *bab, bson_array_builder_t *child); +BSON_EXPORT(bool) +bson_array_builder_append_array_builder_end(bson_array_builder_t *bab, bson_array_builder_t *child); BSON_END_DECLS diff --git a/bsonjs/bson/bson_t-private.h b/bsonjs/bson/bson_t-private.h new file mode 100644 index 0000000..99ddf73 --- /dev/null +++ b/bsonjs/bson/bson_t-private.h @@ -0,0 +1,106 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + + +#ifndef BSON_PRIVATE_H +#define BSON_PRIVATE_H + +#include // IWYU pragma: export + +// + +#include +#include +#include + + +BSON_BEGIN_DECLS + + +typedef enum { + BSON_FLAG_NONE = 0, + BSON_FLAG_INLINE_DATA = (1 << 0), // Set if BSON data is embedded in `bson_t`. + BSON_FLAG_NO_FREE_OBJECT = (1 << 1), // Set if `bson_destroy` should not free `bson_t` object. + BSON_FLAG_RDONLY = (1 << 2), + BSON_FLAG_CHILD = (1 << 3), + BSON_FLAG_IN_CHILD = (1 << 4), + BSON_FLAG_NO_FREE_DATA = (1 << 5), // Set if `bson_destroy` should not free BSON data. +} bson_flags_t; + + +#define BSON_INLINE_DATA_SIZE 120 + + +BSON_ALIGNED_BEGIN(BSON_ALIGN_OF_PTR) +typedef struct { + bson_flags_t flags; + uint32_t len; + uint8_t data[BSON_INLINE_DATA_SIZE]; +} bson_impl_inline_t BSON_ALIGNED_END(BSON_ALIGN_OF_PTR); + + +BSON_STATIC_ASSERT2(impl_inline_t, sizeof(bson_impl_inline_t) == 128); + +typedef struct { + bson_flags_t flags; /* flags describing the bson_t */ + /* len is part of the public bson_t declaration. It is not + * exposed through an accessor function. Plus, it's redundant since + * BSON self describes the length in the first four bytes of the + * buffer. */ + uint32_t len; /* length of bson document in bytes */ + /** + * @brief Pointer to a parent document object if we are a child of some other + * document, otherwise a null pointer. + */ + bson_t *parent; + uint32_t depth; /* Subdocument depth. */ + /** + * @brief If non-null, this pointer refers to the pointer to a buffer that is not directly owned + * by this `bson_t`, but may still manipulated/managed by this `bson_t`. + * + * If this pointer is null, then the data buffer is in `own_buffer` + */ + uint8_t **indirect_buffer; + size_t *indirect_buflen; + /** + * @brief The offset (in bytes) to the beginning of the document within the data buffer. + */ + size_t offset; + /** + * @brief Data buffer that is managed directly by this `bson_t`. This is not used if `indirect_buffer` + * is non-null. + */ + uint8_t *own_buffer; + size_t own_buflen; + bson_realloc_func realloc; /* our realloc implementation */ + void *realloc_func_ctx; /* context for our realloc func */ +} bson_impl_alloc_t; + + +BSON_STATIC_ASSERT2(impl_alloc_t, sizeof(bson_impl_alloc_t) <= 128); + +// Ensure both `bson_t` implementations have the same alignment requirement: +BSON_STATIC_ASSERT2(impls_match_alignment, BSON_ALIGNOF(bson_impl_inline_t) == BSON_ALIGNOF(bson_impl_alloc_t)); +// Ensure `bson_t` has same alignment requirement as implementations: +BSON_STATIC_ASSERT2(impls_match_alignment, BSON_ALIGNOF(bson_t) == BSON_ALIGNOF(bson_impl_alloc_t)); + + +BSON_END_DECLS + + +#endif /* BSON_PRIVATE_H */ diff --git a/bsonjs/bson/bson_t.h b/bsonjs/bson/bson_t.h new file mode 100644 index 0000000..bd0d045 --- /dev/null +++ b/bsonjs/bson/bson_t.h @@ -0,0 +1,61 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BSON_BSON_T_H_INCLUDED +#define BSON_BSON_T_H_INCLUDED + +#include + +#include + +/** + * bson_t: + * + * This structure manages a buffer whose contents are a properly formatted + * BSON document. You may perform various transforms on the BSON documents. + * Additionally, it can be iterated over using bson_iter_t. + * + * See bson_iter_init() for iterating the contents of a bson_t. + * + * When building a bson_t structure using the various append functions, + * memory allocations may occur. That is performed using power of two + * allocations and realloc(). + * + * See http://bsonspec.org for the BSON document spec. + * + * This structure is meant to fit in two sequential 64-byte cachelines. + */ +BSON_ALIGNED_BEGIN(BSON_ALIGN_OF_PTR) 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. */ +} bson_t BSON_ALIGNED_END(BSON_ALIGN_OF_PTR); + +/** + * BSON_INITIALIZER: + * + * This macro can be used to initialize a #bson_t structure on the stack + * without calling bson_init(). + * + * |[ + * bson_t b = BSON_INITIALIZER; + * ]| + */ +#define BSON_INITIALIZER {3, 5, {5}} + +BSON_STATIC_ASSERT2(bson_t, sizeof(bson_t) == 128); + +#endif // BSON_BSON_T_H_INCLUDED diff --git a/bsonjs/bson/bson-compat.h b/bsonjs/bson/compat.h similarity index 68% rename from bsonjs/bson/bson-compat.h rename to bsonjs/bson/compat.h index f403fb4..8fea74c 100644 --- a/bsonjs/bson/bson-compat.h +++ b/bsonjs/bson/compat.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,6 @@ * limitations under the License. */ -#include - - #ifndef BSON_COMPAT_H #define BSON_COMPAT_H @@ -31,59 +28,61 @@ #endif #endif -#include -#include +#include // IWYU pragma: export +#include // IWYU pragma: export #ifdef BSON_OS_WIN32 -#if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0600) +#if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0601) #undef _WIN32_WINNT #endif #ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 +#define _WIN32_WINNT 0x0601 #endif #ifndef NOMINMAX #define NOMINMAX #endif -#include +#include // IWYU pragma: export #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN -#include +#include // IWYU pragma: export #undef WIN32_LEAN_AND_MEAN #else -#include +#include // IWYU pragma: export #endif -#include -#include +#include // IWYU pragma: export +#include // IWYU pragma: export #endif #ifdef BSON_OS_UNIX -#include -#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #endif -#include +#include +#include // IWYU pragma: export +#include // IWYU pragma: export -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include // IWYU pragma: keep: to be removed. +#include // IWYU pragma: keep: to be removed. +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: keep: to be removed. +#include // IWYU pragma: keep: to be removed. +#include // IWYU pragma: keep: to be removed. +#include // IWYU pragma: keep: to be removed. BSON_BEGIN_DECLS #if !defined(_MSC_VER) || (_MSC_VER >= 1800) -#include +#include // IWYU pragma: export #endif #ifdef _MSC_VER #ifndef __cplusplus @@ -133,23 +132,23 @@ typedef SSIZE_T ssize_t; /* 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)) +#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)) +#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))) +#define BSON_NUMERIC_LIMITS_MAX_UNSIGNED(T) ((T)(~((T)0))) #ifndef SSIZE_MAX -#define SSIZE_MAX BSON_NUMERIC_LIMITS_MAX_SIGNED (ssize_t) +#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) +#define SSIZE_MIN BSON_NUMERIC_LIMITS_MIN_SIGNED(ssize_t, SSIZE_MAX) #endif #if defined(__MINGW32__) && !defined(INIT_ONCE_STATIC_INIT) @@ -157,33 +156,9 @@ typedef SSIZE_T ssize_t; typedef RTL_RUN_ONCE INIT_ONCE; #endif -#ifdef BSON_HAVE_STDBOOL_H -#include -#elif !defined(__bool_true_false_are_defined) -#ifndef __cplusplus -typedef signed char bool; -#define false 0 -#define true 1 -#endif -#define __bool_true_false_are_defined 1 -#endif - - -#if defined(__GNUC__) -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) -#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") -#else -#define bson_sync_synchronize() asm volatile ("sync" ::: "memory") -#endif -#elif defined(_MSC_VER) -#define bson_sync_synchronize() MemoryBarrier () -#endif - #if !defined(va_copy) && defined(__va_copy) -#define va_copy(dst, src) __va_copy (dst, src) +#define va_copy(dst, src) __va_copy(dst, src) #endif @@ -202,6 +177,10 @@ typedef signed char bool; #define BSON_IF_MSVC(...) /** Expands the arguments if compiling with GCC or Clang, otherwise empty */ #define BSON_IF_GNU_LIKE(...) __VA_ARGS__ +#else +/** Unsupported compiler. **/ +#define BSON_IF_MSVC(...) +#define BSON_IF_GNU_LIKE(...) #endif #ifdef BSON_OS_WIN32 diff --git a/bsonjs/bson/bson-config.h b/bsonjs/bson/config.h similarity index 90% rename from bsonjs/bson/bson-config.h rename to bsonjs/bson/config.h index baf7e5b..bcb41f7 100644 --- a/bsonjs/bson/bson-config.h +++ b/bsonjs/bson/config.h @@ -128,24 +128,6 @@ #endif -/* - * Define to 1 if you want extra aligned types in libbson - */ -#define BSON_EXTRA_ALIGN 1 -#if BSON_EXTRA_ALIGN != 1 -# undef BSON_EXTRA_ALIGN -#endif - - -/* - * Define to 1 if you have SYS_gettid syscall - */ -#define BSON_HAVE_SYSCALL_TID 0 -#if BSON_HAVE_SYSCALL_TID != 1 -# undef BSON_HAVE_SYSCALL_TID -#endif - - #ifdef MS_WINDOWS # define BSON_HAVE_RAND_R 0 #else @@ -161,4 +143,17 @@ # undef BSON_HAVE_STRLCPY #endif + +/* + * Define to 1 if you have aligned_alloc available on your platform. + */ +#ifdef MS_WINDOWS +# define BSON_HAVE_ALIGNED_ALLOC 0 +#else +# define BSON_HAVE_ALIGNED_ALLOC 1 +#endif +#if BSON_HAVE_ALIGNED_ALLOC != 1 +# undef BSON_HAVE_ALIGNED_ALLOC +#endif + #endif /* BSON_CONFIG_H */ diff --git a/bsonjs/bson/bson-error.c b/bsonjs/bson/error.c similarity index 77% rename from bsonjs/bson/bson-error.c rename to bsonjs/bson/error.c index 0c6257b..29c650b 100644 --- a/bsonjs/bson/bson-error.c +++ b/bsonjs/bson/error.c @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,16 @@ */ -#include -#include +#include +#include -#include -#include -#include -#include -#include #include +#include +#include +#include + +#include +#include // See `bson_strerror_r()` definition below. #if !defined(_WIN32) && !defined(__APPLE__) @@ -63,23 +64,24 @@ */ void -bson_set_error (bson_error_t *error, /* OUT */ - uint32_t domain, /* IN */ - uint32_t code, /* IN */ - const char *format, /* IN */ - ...) /* IN */ +bson_set_error(bson_error_t *error, /* OUT */ + uint32_t domain, /* IN */ + uint32_t code, /* IN */ + const char *format, /* IN */ + ...) /* IN */ { va_list args; if (error) { error->domain = domain; error->code = code; + bson_set_error_category(error, BSON_ERROR_CATEGORY); - va_start (args, format); - bson_vsnprintf (error->message, sizeof error->message, format, args); - va_end (args); - - error->message[sizeof error->message - 1] = '\0'; + va_start(args, format); + char buffer[sizeof error->message]; + bson_vsnprintf(buffer, sizeof error->message, format, args); + memcpy(error->message, buffer, sizeof buffer); + va_end(args); } } @@ -103,9 +105,9 @@ bson_set_error (bson_error_t *error, /* OUT */ */ char * -bson_strerror_r (int err_code, /* IN */ - char *buf BSON_MAYBE_UNUSED, /* IN */ - size_t buflen BSON_MAYBE_UNUSED) /* IN */ +bson_strerror_r(int err_code, /* IN */ + char *buf BSON_MAYBE_UNUSED, /* IN */ + size_t buflen BSON_MAYBE_UNUSED) /* IN */ { static const char *unknown_msg = "Unknown error"; char *ret = NULL; @@ -113,13 +115,13 @@ bson_strerror_r (int err_code, /* IN */ #if defined(_WIN32) // Windows does not provide `strerror_l` or `strerror_r`, but it does // unconditionally provide `strerror_s`. - if (strerror_s (buf, buflen, err_code) != 0) { + if (strerror_s(buf, buflen, err_code) == 0) { ret = buf; } #elif defined(_AIX) // AIX does not provide strerror_l, and its strerror_r isn't glibc's. // But it does provide a glibc compatible one called __linux_strerror_r - ret = __linux_strerror_r (err_code, buf, buflen); + ret = __linux_strerror_r(err_code, buf, buflen); #elif defined(__APPLE__) // Apple does not provide `strerror_l`, but it does unconditionally provide // the XSI-compliant `strerror_r`, but only when compiling with Apple Clang. @@ -129,20 +131,21 @@ bson_strerror_r (int err_code, /* IN */ // error message string even when `strerror_r` fails, as encouraged (but not // required) by the POSIX spec (see: // https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html#tag_16_574_08). - (void) strerror_r (err_code, buf, buflen); + (void)strerror_r(err_code, buf, buflen); + ret = buf; #elif defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700 // The behavior (of `strerror_l`) is undefined if the locale argument to // `strerror_l()` is the special locale object LC_GLOBAL_LOCALE or is not a // valid locale object handle. - locale_t locale = uselocale ((locale_t) 0); + locale_t locale = uselocale((locale_t)0); // No need to test for error (it can only be [EINVAL]). if (locale == LC_GLOBAL_LOCALE) { // Only use our own locale if a thread-local locale was not already set. // This is just to satisfy `strerror_l`. We do NOT want to unconditionally // set a thread-local locale. - locale = newlocale (LC_MESSAGES_MASK, "C", (locale_t) 0); + locale = newlocale(LC_MESSAGES_MASK, "C", (locale_t)0); } - BSON_ASSERT (locale != LC_GLOBAL_LOCALE); + BSON_ASSERT(locale != LC_GLOBAL_LOCALE); // Avoid `strerror_r` compatibility headaches with GNU extensions and the // musl library by using `strerror_l` instead. Furthermore, `strerror_r` is @@ -153,15 +156,15 @@ bson_strerror_r (int err_code, /* IN */ // POSIX Spec: since strerror_l() is required to return a string for some // errors, an application wishing to check for all error situations should // set errno to 0, then call strerror_l(), then check errno. - if (locale != (locale_t) 0) { + if (locale != (locale_t)0) { errno = 0; - ret = strerror_l (err_code, locale); + ret = strerror_l(err_code, locale); if (errno != 0) { ret = NULL; } - freelocale (locale); + freelocale(locale); } else { // Could not obtain a valid `locale_t` object to satisfy `strerror_l`. // Fallback to `bson_strncpy` below. @@ -169,13 +172,13 @@ bson_strerror_r (int err_code, /* IN */ #elif defined(_GNU_SOURCE) // Unlikely, but continue supporting use of GNU extension in cases where the // C Driver is being built without _XOPEN_SOURCE=700. - ret = strerror_r (err_code, buf, buflen); + ret = strerror_r(err_code, buf, buflen); #else #error "Unable to find a supported strerror_r candidate" #endif if (!ret) { - bson_strncpy (buf, unknown_msg, buflen); + bson_strncpy(buf, unknown_msg, buflen); ret = buf; } diff --git a/bsonjs/bson/error.h b/bsonjs/bson/error.h new file mode 100644 index 0000000..f31fa7b --- /dev/null +++ b/bsonjs/bson/error.h @@ -0,0 +1,94 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BSON_ERROR_T_INCLUDED +#define BSON_ERROR_T_INCLUDED + +#include + +#include + +BSON_BEGIN_DECLS + +#define BSON_ERROR_BUFFER_SIZE 503 + +BSON_ALIGNED_BEGIN(BSON_ALIGN_OF_PTR) // Aligned for backwards-compatibility. +typedef struct _bson_error_t { + uint32_t domain; + uint32_t code; + char message[BSON_ERROR_BUFFER_SIZE]; + uint8_t reserved; // For internal use only! +} bson_error_t BSON_ALIGNED_END(BSON_ALIGN_OF_PTR); + + +BSON_STATIC_ASSERT2(error_t, sizeof(bson_error_t) == 512); + +#define BSON_ERROR_JSON 1 +#define BSON_ERROR_READER 2 +#define BSON_ERROR_INVALID 3 +#define BSON_ERROR_VECTOR 4 + +BSON_EXPORT(void) +bson_set_error(bson_error_t *error, uint32_t domain, uint32_t code, const char *format, ...) BSON_GNUC_PRINTF(4, 5); + +BSON_EXPORT(char *) +bson_strerror_r(int err_code, char *buf, size_t buflen); + +/** + * @brief Reset the content of a bson_error_t to indicate no error. + * + * @param error Pointer to an error to be overwritten. If null, this function + * has no effect. + * + * This is static-inline because it is trivially optimizable as a (conditional) + * `memset`. + */ +static inline void +bson_error_clear(bson_error_t *error) +{ + if (!error) { + return; + } + // Statically initialized to a zero struct: + static bson_error_t zero_error; + // Replace the caller's value: + *error = zero_error; +} + +/** + * @brief Given a `bson_error_t` pointer l-value, ensure that it is non-null, and clear any + * error value that it might hold. + * + * @param ErrorPointer An l-value expression of type `bson_error_t*`. + * + * If the passed pointer is null, then it will be updated to point to an anonymous + * `bson_error_t` object that lives in the caller's scope. + * + * @note This macro is not valid in C++ because it relies on C99 compound literal semantics + */ +#define bson_error_reset(ErrorPointer) bson_error_reset(&(ErrorPointer), &(bson_error_t){0}) +static inline void(bson_error_reset)(bson_error_t **error, bson_error_t *localptr) +{ + if (*error == NULL) { + *error = localptr; + } + bson_error_clear(*error); +} + +BSON_END_DECLS + + +#endif // BSON_ERROR_T_INCLUDED diff --git a/bsonjs/bson/bson-macros.h b/bsonjs/bson/macros.h similarity index 50% rename from bsonjs/bson/bson-macros.h rename to bsonjs/bson/macros.h index f926391..b9c8480 100644 --- a/bsonjs/bson/bson-macros.h +++ b/bsonjs/bson/macros.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,21 +14,18 @@ * limitations under the License. */ -#include - - #ifndef BSON_MACROS_H #define BSON_MACROS_H +#include // IWYU pragma: export #include +#include #ifdef __cplusplus #include #endif -#include - #if BSON_OS == 1 #define BSON_OS_UNIX @@ -79,9 +76,9 @@ #ifdef BSON_STATIC #define BSON_API #elif defined(BSON_COMPILATION) -#define BSON_API __declspec (dllexport) +#define BSON_API __declspec(dllexport) #else -#define BSON_API __declspec (dllimport) +#define BSON_API __declspec(dllimport) #endif #define BSON_CALL __cdecl @@ -92,7 +89,7 @@ #ifdef BSON_STATIC #define BSON_API #elif defined(BSON_COMPILATION) -#define BSON_API __attribute__ ((visibility ("default"))) +#define BSON_API __attribute__((visibility("default"))) #else #define BSON_API #endif @@ -113,7 +110,7 @@ #ifdef MIN #define BSON_MIN MIN #elif defined(__cplusplus) -#define BSON_MIN(a, b) ((std::min) (a, b)) +#define BSON_MIN(a, b) ((std::min)(a, b)) #elif defined(_MSC_VER) #define BSON_MIN(a, b) ((a) < (b) ? (a) : (b)) #else @@ -124,7 +121,7 @@ #ifdef MAX #define BSON_MAX MAX #elif defined(__cplusplus) -#define BSON_MAX(a, b) ((std::max) (a, b)) +#define BSON_MAX(a, b) ((std::max)(a, b)) #elif defined(_MSC_VER) #define BSON_MAX(a, b) ((a) > (b) ? (a) : (b)) #else @@ -139,14 +136,14 @@ #endif #if defined(__cplusplus) && (__cplusplus >= 201103L || defined(_MSVC_LANG)) -#define BSON_ALIGNOF(expr) alignof (expr) +#define BSON_ALIGNOF(expr) alignof(expr) #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L -#define BSON_ALIGNOF(expr) _Alignof (expr) +#define BSON_ALIGNOF(expr) _Alignof(expr) #else #if defined(_MSC_VER) -#define BSON_ALIGNOF(expr) __alignof (expr) +#define BSON_ALIGNOF(expr) __alignof(expr) #else -#define BSON_ALIGNOF(expr) __alignof__ (expr) +#define BSON_ALIGNOF(expr) __alignof__(expr) #endif #endif // __STDC_VERSION__ >= 201112L @@ -158,25 +155,15 @@ #define BSON_ALIGN_OF_PTR 4 #endif #else -#define BSON_ALIGN_OF_PTR (BSON_ALIGNOF (void *)) +#define BSON_ALIGN_OF_PTR (BSON_ALIGNOF(void *)) #endif -#ifdef BSON_EXTRA_ALIGN -#if defined(_MSC_VER) -#define BSON_ALIGNED_BEGIN(_N) __declspec (align (_N)) -#define BSON_ALIGNED_END(_N) -#else -#define BSON_ALIGNED_BEGIN(_N) -#define BSON_ALIGNED_END(_N) __attribute__ ((aligned (_N))) -#endif -#else #if defined(_MSC_VER) -#define BSON_ALIGNED_BEGIN(_N) __declspec (align (BSON_ALIGN_OF_PTR)) +#define BSON_ALIGNED_BEGIN(_N) __declspec(align(BSON_ALIGN_OF_PTR)) #define BSON_ALIGNED_END(_N) #else #define BSON_ALIGNED_BEGIN(_N) -#define BSON_ALIGNED_END(_N) __attribute__ ((aligned ((_N) > BSON_ALIGN_OF_PTR ? BSON_ALIGN_OF_PTR : (_N)))) -#endif +#define BSON_ALIGNED_END(_N) __attribute__((aligned((_N) > BSON_ALIGN_OF_PTR ? BSON_ALIGN_OF_PTR : (_N)))) #endif @@ -190,24 +177,64 @@ #define BSON_FUNC __func__ #endif -#define BSON_ASSERT(test) \ - do { \ - if (!(BSON_LIKELY (test))) { \ - fprintf (stderr, "%s:%d %s(): precondition failed: %s\n", __FILE__, __LINE__, BSON_FUNC, #test); \ - abort (); \ - } \ + +#if defined(_MSC_VER) +#define BSON_INLINE __inline +#else +#define BSON_INLINE __inline__ +#endif + + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L +#define BSON_NORETURN [[noreturn]] +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +#define BSON_NORETURN _Noreturn +#elif defined(__GNUC__) && 2 < __GNUC__ + (8 <= __GNUC_MINOR__) +#define BSON_NORETURN __attribute__((__noreturn__)) +#else +#define BSON_NORETURN +#endif + + +#if defined(__GNUC__) +#define BSON_RESTRICT __restrict__ +#elif defined(_MSC_VER) +#define BSON_RESTRICT __restrict +#elif !defined(__cplusplus) +// C99 (not C++) +#define BSON_RESTRICT restrict +#else +#define BSON_RESTRICT +#endif + + +BSON_NORETURN static BSON_INLINE void +_bson_assert_failed_on_line(const char *file, int line, const char *func, const char *test) +{ + fprintf(stderr, "%s:%d %s(): assertion failed: %s\n", file, line, func, test); + abort(); +} + +BSON_NORETURN static BSON_INLINE void +_bson_assert_failed_on_param(const char *param, const char *func) +{ + fprintf(stderr, "The parameter: %s, in function %s, cannot be NULL\n", param, func); + abort(); +} + +#define BSON_ASSERT(test) \ + do { \ + if (!(BSON_LIKELY(test))) { \ + _bson_assert_failed_on_line(__FILE__, (int)(__LINE__), BSON_FUNC, #test); \ + } \ } while (0) /** * @brief Assert the expression `Assertion`, and evaluates to `Value` on * success. */ -#define BSON_ASSERT_INLINE(Assertion, Value) \ - ((void) ((Assertion) \ - ? (0) \ - : ((fprintf (stderr, "%s:%d %s(): Assertion '%s' failed", __FILE__, __LINE__, BSON_FUNC, #Assertion), \ - abort ()), \ - 0)), \ +#define BSON_ASSERT_INLINE(Assertion, Value) \ + ((void)((Assertion) ? (0) : (_bson_assert_failed_on_line(__FILE__, (int)(__LINE__), BSON_FUNC, #Assertion), 0)), \ Value) /** @@ -221,34 +248,37 @@ * bar* b = BSON_ASSERT_PTR_INLINE(f)->bar_value; * ``` */ -#define BSON_ASSERT_PTR_INLINE(Pointer) BSON_ASSERT_INLINE ((Pointer) != NULL, (Pointer)) +#define BSON_ASSERT_PTR_INLINE(Pointer) BSON_ASSERT_INLINE((Pointer) != NULL, (Pointer)) /* Used for asserting parameters to provide a more precise error message */ -#define BSON_ASSERT_PARAM(param) \ - do { \ - if ((BSON_UNLIKELY (param == NULL))) { \ - fprintf (stderr, "The parameter: %s, in function %s, cannot be NULL\n", #param, BSON_FUNC); \ - abort (); \ - } \ +#define BSON_ASSERT_PARAM(param) \ + do { \ + if ((BSON_UNLIKELY(param == NULL))) { \ + _bson_assert_failed_on_param(#param, BSON_FUNC); \ + } \ } while (0) +// `BSON_OPTIONAL_PARAM` is a documentation-only macro to document X may be NULL. +// Useful in combination with `BSON_ASSERT_PARAM` to document and assert pointer parameters. +#define BSON_OPTIONAL_PARAM(param) ((void)param) + /* obsolete macros, preserved for compatibility */ -#define BSON_STATIC_ASSERT(s) BSON_STATIC_ASSERT_ (s, __LINE__) -#define BSON_STATIC_ASSERT_JOIN(a, b) BSON_STATIC_ASSERT_JOIN2 (a, b) +#define BSON_STATIC_ASSERT(s) BSON_STATIC_ASSERT_(s, __LINE__) +#define BSON_STATIC_ASSERT_JOIN(a, b) BSON_STATIC_ASSERT_JOIN2(a, b) #define BSON_STATIC_ASSERT_JOIN2(a, b) a##b -#define BSON_STATIC_ASSERT_(s, l) typedef char BSON_STATIC_ASSERT_JOIN (static_assert_test_, __LINE__)[(s) ? 1 : -1] +#define BSON_STATIC_ASSERT_(s, l) typedef char BSON_STATIC_ASSERT_JOIN(static_assert_test_, __LINE__)[(s) ? 1 : -1] /* modern macros */ -#define BSON_STATIC_ASSERT2(_name, _s) BSON_STATIC_ASSERT2_ (_s, __LINE__, _name) -#define BSON_STATIC_ASSERT_JOIN3(_a, _b, _name) BSON_STATIC_ASSERT_JOIN4 (_a, _b, _name) +#define BSON_STATIC_ASSERT2(_name, _s) BSON_STATIC_ASSERT2_(_s, __LINE__, _name) +#define BSON_STATIC_ASSERT_JOIN3(_a, _b, _name) BSON_STATIC_ASSERT_JOIN4(_a, _b, _name) #define BSON_STATIC_ASSERT_JOIN4(_a, _b, _name) _a##_b##_name #define BSON_STATIC_ASSERT2_(_s, _l, _name) \ - typedef char BSON_STATIC_ASSERT_JOIN3 (static_assert_test_, __LINE__, _name)[(_s) ? 1 : -1] + typedef char BSON_STATIC_ASSERT_JOIN3(static_assert_test_, __LINE__, _name)[(_s) ? 1 : -1] #if defined(__GNUC__) -#define BSON_GNUC_PURE __attribute__ ((pure)) -#define BSON_GNUC_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result)) +#define BSON_GNUC_PURE __attribute__((pure)) +#define BSON_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) #else #define BSON_GNUC_PURE #define BSON_GNUC_WARN_UNUSED_RESULT @@ -256,8 +286,8 @@ #if BSON_GNUC_CHECK_VERSION(4, 0) && !defined(_WIN32) -#define BSON_GNUC_NULL_TERMINATED __attribute__ ((sentinel)) -#define BSON_GNUC_INTERNAL __attribute__ ((visibility ("hidden"))) +#define BSON_GNUC_NULL_TERMINATED __attribute__((sentinel)) +#define BSON_GNUC_INTERNAL __attribute__((visibility("hidden"))) #else #define BSON_GNUC_NULL_TERMINATED #define BSON_GNUC_INTERNAL @@ -265,8 +295,8 @@ #if defined(__GNUC__) -#define BSON_LIKELY(x) __builtin_expect (!!(x), 1) -#define BSON_UNLIKELY(x) __builtin_expect (!!(x), 0) +#define BSON_LIKELY(x) __builtin_expect(!!(x), 1) +#define BSON_UNLIKELY(x) __builtin_expect(!!(x), 0) #else #define BSON_LIKELY(v) v #define BSON_UNLIKELY(v) v @@ -274,9 +304,9 @@ #if defined(__clang__) -#define BSON_GNUC_PRINTF(f, v) __attribute__ ((format (printf, f, v))) +#define BSON_GNUC_PRINTF(f, v) __attribute__((format(printf, f, v))) #elif BSON_GNUC_CHECK_VERSION(4, 4) -#define BSON_GNUC_PRINTF(f, v) __attribute__ ((format (gnu_printf, f, v))) +#define BSON_GNUC_PRINTF(f, v) __attribute__((format(gnu_printf, f, v))) #else #define BSON_GNUC_PRINTF(f, v) #endif @@ -289,38 +319,47 @@ #endif -#if defined(_MSC_VER) -#define BSON_INLINE __inline -#else -#define BSON_INLINE __inline__ -#endif - - #ifdef _MSC_VER #define BSON_ENSURE_ARRAY_PARAM_SIZE(_n) #define BSON_TYPEOF decltype #else -#define BSON_ENSURE_ARRAY_PARAM_SIZE(_n) static (_n) +#define BSON_ENSURE_ARRAY_PARAM_SIZE(_n) static(_n) #define BSON_TYPEOF typeof #endif +/** + * @brief Statically annotate an entity as deprecated, including the given deprecation message + * + * @param Message The message to be included in a deprecation warning. This + * should be a string literal. + */ +#define BSON_DEPRECATED(Message) _bsonDeprecatedImpl(Message) -#if BSON_GNUC_CHECK_VERSION(3, 1) -#define BSON_GNUC_DEPRECATED __attribute__ ((__deprecated__)) +// Pick the appropriate implementation of a deprecation attribute +#if defined(_MSC_VER) +// For MSVC, emit __declspec(deprecated(Msg)) +#define _bsonDeprecatedImpl(Msg) __declspec(deprecated(Msg)) +#elif defined(__GNUC__) && (defined(__clang__) || BSON_GNUC_CHECK_VERSION(4, 5)) +// For new enough Clang and GCC, emit __attribute__((__deprecated__(Msg))) +#define _bsonDeprecatedImpl(Msg) __attribute__((__deprecated__(Msg))) +#elif defined(__GNUC__) +// For older GCC, emit deprecation attribute without the message +#define _bsonDeprecatedImpl(Msg) __attribute__((__deprecated__)) #else -#define BSON_GNUC_DEPRECATED +// For other compilers, emit nothing +#define _bsonDeprecatedImpl(Msg) #endif +#define BSON_DEPRECATED_FOR(F) BSON_DEPRECATED("This API is deprecated. Use " #F " instead.") + +#define BSON_GNUC_DEPRECATED BSON_DEPRECATED("This API is deprecated") +#define BSON_GNUC_DEPRECATED_FOR(F) BSON_DEPRECATED_FOR(F) + #define BSON_CONCAT_IMPL(a, ...) a##__VA_ARGS__ -#define BSON_CONCAT(a, ...) BSON_CONCAT_IMPL (a, __VA_ARGS__) -#define BSON_CONCAT3(a, b, c) BSON_CONCAT (a, BSON_CONCAT (b, c)) -#define BSON_CONCAT4(a, b, c, d) BSON_CONCAT (BSON_CONCAT (a, b), BSON_CONCAT (c, d)) +#define BSON_CONCAT(a, ...) BSON_CONCAT_IMPL(a, __VA_ARGS__) +#define BSON_CONCAT3(a, b, c) BSON_CONCAT(a, BSON_CONCAT(b, c)) +#define BSON_CONCAT4(a, b, c, d) BSON_CONCAT(BSON_CONCAT(a, b), BSON_CONCAT(c, d)) -#if BSON_GNUC_CHECK_VERSION(4, 5) -#define BSON_GNUC_DEPRECATED_FOR(f) __attribute__ ((deprecated ("Use " #f " instead"))) -#else -#define BSON_GNUC_DEPRECATED_FOR(f) BSON_GNUC_DEPRECATED -#endif /** * @brief String-ify the given argument @@ -333,7 +372,7 @@ * Does nothing on MSVC. */ #if defined(__GNUC__) || defined(__clang__) -#define BSON_MAYBE_UNUSED __attribute__ ((unused)) +#define BSON_MAYBE_UNUSED __attribute__((unused)) #else #define BSON_MAYBE_UNUSED /* Nothing for other compilers */ #endif @@ -345,10 +384,10 @@ * @param What A string to include in the error message if this point is ever * executed. */ -#define BSON_UNREACHABLE(What) \ - do { \ - fprintf (stderr, "%s:%d %s(): Unreachable code reached: %s\n", __FILE__, __LINE__, BSON_FUNC, What); \ - abort (); \ +#define BSON_UNREACHABLE(What) \ + do { \ + fprintf(stderr, "%s:%d %s(): Unreachable code reached: %s\n", __FILE__, (int)(__LINE__), BSON_FUNC, What); \ + abort(); \ } while (0) /** @@ -359,7 +398,20 @@ */ #define BSON_UNUSED(expr) \ do { \ - (void) (expr); \ + (void)(expr); \ } while (0) +// Disable the -Wunsafe-buffer-usage warning. +#define BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN +#define BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END +#if defined(__clang__) +#if __has_warning("-Wunsafe-buffer-usage") +#undef BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN +#undef BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END +#define BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN \ + _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wunsafe-buffer-usage\"") +#define BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END _Pragma("clang diagnostic pop") +#endif // __has_warning("-Wunsafe-buffer-usage") +#endif // defined(__clang__) + #endif /* BSON_MACROS_H */ diff --git a/bsonjs/bson/bson-memory.c b/bsonjs/bson/memory.c similarity index 63% rename from bsonjs/bson/bson-memory.c rename to bsonjs/bson/memory.c index 6c37003..0d84ea5 100644 --- a/bsonjs/bson/bson-memory.c +++ b/bsonjs/bson/memory.c @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,34 +15,39 @@ */ -#include -#include -#include -#include +#include -#include -#include -#include +#include +#include + +#include +#include + +#include +#include +#include +#include // Ensure size of exported structs are stable. -BSON_STATIC_ASSERT2 (bson_mem_vtable_t, sizeof (bson_mem_vtable_t) == sizeof (void *) * 8u); +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__) && \ - !defined(_AIX) +_aligned_alloc_impl(size_t alignment, size_t num_bytes) +#if defined(BSON_HAVE_ALIGNED_ALLOC) { - return aligned_alloc (alignment, num_bytes); + MC_DISABLE_IMPLICIT_WARNING_BEGIN + return aligned_alloc(alignment, num_bytes); + MC_DISABLE_IMPLICIT_WARNING_END } #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); + BSON_MAYBE_UNUSED int ret = posix_memalign(&mem, alignment, num_bytes); return mem; } @@ -52,18 +57,20 @@ _aligned_alloc_impl (size_t alignment, size_t num_bytes) // 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); + BSON_UNUSED(alignment); + return malloc(num_bytes); } #endif - +mlib_diagnostic_push(); +mlib_msvc_warning(disable : 4232); static bson_mem_vtable_t gMemVtable = {.malloc = malloc, .calloc = calloc, .realloc = realloc, .free = free, .aligned_alloc = _aligned_alloc_impl, .padding = {0}}; +mlib_diagnostic_pop(); /* @@ -91,14 +98,14 @@ static bson_mem_vtable_t gMemVtable = {.malloc = malloc, */ void * -bson_malloc (size_t num_bytes) /* IN */ +bson_malloc(size_t num_bytes) /* IN */ { void *mem = NULL; - 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); - abort (); + 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); + abort(); } } @@ -129,14 +136,14 @@ bson_malloc (size_t num_bytes) /* IN */ */ void * -bson_malloc0 (size_t num_bytes) /* IN */ +bson_malloc0(size_t num_bytes) /* IN */ { void *mem = NULL; - 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); - abort (); + 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); + abort(); } } @@ -171,14 +178,14 @@ bson_malloc0 (size_t num_bytes) /* IN */ */ void * -bson_aligned_alloc (size_t alignment /* IN */, size_t num_bytes /* IN */) +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 (); + 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(); } } @@ -209,18 +216,91 @@ bson_aligned_alloc (size_t alignment /* IN */, size_t num_bytes /* IN */) */ void * -bson_aligned_alloc0 (size_t alignment /* IN */, size_t num_bytes /* IN */) +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 (); + 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); + memset(mem, 0, num_bytes); + } + + return mem; +} + + +/* + *-------------------------------------------------------------------------- + * + * bson_array_alloc -- + * + * Allocates memory for an array of objects. + * + * Libbson does not try to handle OOM conditions as it is beyond the + * scope of this library to handle so appropriately. + * + * Parameters: + * @num_elems: The number of objects to allocate. + * @elem_size: The size of each object in bytes. + * + * Returns: + * A pointer if successful; otherwise abort() is called and this + * function will never return. + * + *-------------------------------------------------------------------------- + */ + +void * +bson_array_alloc(size_t num_elems /* IN */, size_t elem_size /* IN */) +{ + void *mem = NULL; + size_t num_bytes = 0; + BSON_ASSERT(!mlib_mul(&num_bytes, num_elems, elem_size)); + + if (BSON_LIKELY(num_bytes)) { + mem = bson_malloc(num_bytes); } + return mem; +} + +/* + *-------------------------------------------------------------------------- + * + * bson_array_alloc0-- + * + * Like bson_array_alloc() except the memory is zeroed after allocation + * for convenience. + * + * Parameters: + * @num_elems: The number of objects to allocate. + * @elem_size: The size of each object in bytes. + * + * Returns: + * A pointer if successful; otherwise abort() is called and this + * function will never return. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +void * +bson_array_alloc0(size_t num_elems /* IN */, size_t elem_size /* IN */) +{ + void *mem = NULL; + size_t num_bytes = 0; + BSON_ASSERT(!mlib_mul(&num_bytes, num_elems, elem_size)); + if (BSON_LIKELY(num_bytes)) { + if (BSON_UNLIKELY(!(mem = gMemVtable.calloc(num_elems, elem_size)))) { + fprintf(stderr, "Failure to allocate memory in bson_array_alloc0(). errno: %d.\n", errno); + abort(); + } + } return mem; } @@ -248,24 +328,24 @@ bson_aligned_alloc0 (size_t alignment /* IN */, size_t num_bytes /* IN */) */ void * -bson_realloc (void *mem, /* IN */ - size_t num_bytes) /* IN */ +bson_realloc(void *mem, /* IN */ + size_t num_bytes) /* IN */ { /* * Not all platforms are guaranteed to free() the memory if a call to * realloc() with a size of zero occurs. Windows, Linux, and FreeBSD do, * however, OS X does not. */ - if (BSON_UNLIKELY (num_bytes == 0)) { - gMemVtable.free (mem); + if (BSON_UNLIKELY(num_bytes == 0)) { + gMemVtable.free(mem); return NULL; } - mem = gMemVtable.realloc (mem, num_bytes); + mem = gMemVtable.realloc(mem, num_bytes); - if (BSON_UNLIKELY (!mem)) { - fprintf (stderr, "Failure to re-allocate memory in bson_realloc(). errno: %d.\n", errno); - abort (); + if (BSON_UNLIKELY(!mem)) { + fprintf(stderr, "Failure to re-allocate memory in bson_realloc(). errno: %d.\n", errno); + abort(); } return mem; @@ -297,13 +377,13 @@ bson_realloc (void *mem, /* IN */ void * -bson_realloc_ctx (void *mem, /* IN */ - size_t num_bytes, /* IN */ - void *ctx) /* IN */ +bson_realloc_ctx(void *mem, /* IN */ + size_t num_bytes, /* IN */ + void *ctx) /* IN */ { - BSON_UNUSED (ctx); + BSON_UNUSED(ctx); - return bson_realloc (mem, num_bytes); + return bson_realloc(mem, num_bytes); } @@ -330,9 +410,9 @@ bson_realloc_ctx (void *mem, /* IN */ */ void -bson_free (void *mem) /* IN */ +bson_free(void *mem) /* IN */ { - gMemVtable.free (mem); + gMemVtable.free(mem); } @@ -359,22 +439,22 @@ bson_free (void *mem) /* IN */ */ void -bson_zero_free (void *mem, /* IN */ - size_t size) /* IN */ +bson_zero_free(void *mem, /* IN */ + size_t size) /* IN */ { - if (BSON_LIKELY (mem)) { - memset (mem, 0, size); - gMemVtable.free (mem); + if (BSON_LIKELY(mem)) { + memset(mem, 0, size); + gMemVtable.free(mem); } } static void * -_aligned_alloc_as_malloc (size_t alignment, size_t num_bytes) +_aligned_alloc_as_malloc(size_t alignment, size_t num_bytes) { - BSON_UNUSED (alignment); + BSON_UNUSED(alignment); - return gMemVtable.malloc (num_bytes); + return gMemVtable.malloc(num_bytes); } @@ -399,14 +479,14 @@ _aligned_alloc_as_malloc (size_t alignment, size_t num_bytes) */ void -bson_mem_set_vtable (const bson_mem_vtable_t *vtable) +bson_mem_set_vtable(const bson_mem_vtable_t *vtable) { - BSON_ASSERT (vtable); + BSON_ASSERT(vtable); if (!vtable->malloc || !vtable->calloc || !vtable->realloc || !vtable->free) { - fprintf (stderr, - "Failure to install BSON vtable, " - "missing functions.\n"); + fprintf(stderr, + "Failure to install BSON vtable, " + "missing functions.\n"); return; } @@ -419,7 +499,7 @@ bson_mem_set_vtable (const bson_mem_vtable_t *vtable) } void -bson_mem_restore_vtable (void) +bson_mem_restore_vtable(void) { bson_mem_vtable_t vtable = {.malloc = malloc, .calloc = calloc, @@ -428,5 +508,5 @@ bson_mem_restore_vtable (void) .aligned_alloc = _aligned_alloc_impl, .padding = {0}}; - bson_mem_set_vtable (&vtable); + bson_mem_set_vtable(&vtable); } diff --git a/bsonjs/bson/memory.h b/bsonjs/bson/memory.h new file mode 100644 index 0000000..1c8fc5f --- /dev/null +++ b/bsonjs/bson/memory.h @@ -0,0 +1,71 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BSON_MEMORY_H_INCLUDED +#define BSON_MEMORY_H_INCLUDED + +#include + + +BSON_BEGIN_DECLS + + +typedef void *(BSON_CALL *bson_realloc_func)(void *mem, size_t num_bytes, void *ctx); + +typedef struct _bson_mem_vtable_t { + void *(BSON_CALL *malloc)(size_t num_bytes); + void *(BSON_CALL *calloc)(size_t n_members, size_t num_bytes); + void *(BSON_CALL *realloc)(void *mem, size_t num_bytes); + void(BSON_CALL *free)(void *mem); + void *(BSON_CALL *aligned_alloc)(size_t alignment, size_t num_bytes); + void *padding[3]; +} bson_mem_vtable_t; + + +BSON_EXPORT(void) +bson_mem_set_vtable(const bson_mem_vtable_t *vtable); +BSON_EXPORT(void) +bson_mem_restore_vtable(void); +BSON_EXPORT(void *) +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_array_alloc(size_t num_elems, size_t elem_size); +BSON_EXPORT(void *) +bson_array_alloc0(size_t num_elems, size_t elem_size); +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); +BSON_EXPORT(void) +bson_free(void *mem); +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)))) +#define BSON_ARRAY_ALLOC(N, T) ((T *)(bson_array_alloc(N, sizeof(T)))) +#define BSON_ARRAY_ALLOC0(N, T) ((T *)(bson_array_alloc0(N, sizeof(T)))) + +BSON_END_DECLS + +#endif // BSON_MEMORY_H_INCLUDED diff --git a/bsonjs/bson/validate-private.h b/bsonjs/bson/validate-private.h new file mode 100644 index 0000000..440cfd8 --- /dev/null +++ b/bsonjs/bson/validate-private.h @@ -0,0 +1,37 @@ +#ifndef BSON_VALIDATE_PRIVATE_H_INCLUDED +#define BSON_VALIDATE_PRIVATE_H_INCLUDED + +#include + +enum { + /** + * @brief This compile-time constant represents the maximum document nesting + * depth permitted by the `bson_validate` family of functions. If the nesting + * depth exceeds this limit, the data will be rejected. + * + * This limit is intentionally larger than the default limit of MongoDB + * server, since we cannot anticipate what a libbson user might actually want + * to do with BSON, and to prevent accidentally rejecting data that the + * server might accept. The main purpose of this limit is to prevent stack + * overflow, not to reject invalid data. + */ + BSON_VALIDATION_MAX_NESTING_DEPTH = 500, +}; + +/** + * @brief Private function backing the implementation of validation. + * + * Validation was previously defined in the overburdened `bson-iter.c`, but it + * is now defined in its own file. + * + * @param bson The document to validate. Must be non-null. + * @param flags Validation control flags + * @param offset Receives the offset at which validation failed. Must be non-null. + * @param error Receives the error describing why validation failed. Must be non-null. + * @return true If the given document has no validation errors + * @return false Otherwise + */ +bool +_bson_validate_impl_v2(const bson_t *bson, bson_validate_flags_t flags, size_t *offset, bson_error_t *error); + +#endif // BSON_VALIDATE_PRIVATE_H_INCLUDED diff --git a/bsonjs/bson/validate.c b/bsonjs/bson/validate.c new file mode 100644 index 0000000..591d591 --- /dev/null +++ b/bsonjs/bson/validate.c @@ -0,0 +1,568 @@ +/** + * @file bson/validate.c + * @brief Implementation of BSON document validation + * @date 2025-05-28 + * + * This file implements the backend for the `bson_validate` family of functions. + * + * The `_validate_...` functions all accept `validator* self` as their first parameter, + * and must `return false` AND set `self->error` if-and-only-if they encounter a validation error. + * If a function returns true, it is assumed that validation of that item succeeded. + * + * For brevity, the `require...` macros are defined, which check conditions, set errors, + * and `return false` inline. + * + * @copyright Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +#include +#include + +#include +#include + +/** + * @brief User parameters for validation behavior. These correspond to the various + * flags that can be given when the user requests validation + */ +typedef struct { + /** + * @brief Should we allow invalid UTF-8 in string components? + * + * This affects the behavior of validation of key strings and string-like + * elements that require UTF-8 encoding. + * + * Technically invalid UTF-8 is invalid in BSON, but applications may already + * rely on this being accepted. + */ + bool allow_invalid_utf8; + /** + * @brief Should we allow a zero-valued codepoint in text? + * + * Unicode U+0000 is a valid codepoint, but a lot of software doesn't like + * it and handles it poorly. By default, we reject it, but the user may + * want to allow it. + * + * Note that because element keys rely on null termination, element keys + * cannot contain U+0000 by construction. + */ + bool allow_null_in_utf8; + /// Should we allow element key strings to be empty strings? + bool allow_empty_keys; + /// Should we allow ASCII dot "." in element key strings? + bool allow_dot_in_keys; + /** + * @brief Check for special element keys that begin with an ASCII dollar "$" + * + * By default, we ignore them and treat them as regular elements. If this is + * enabled, we reject key strings that start with a dollar, unless it is a + * special extended JSON DBRef document. + * + * This also enables DBRef validation, which checks the structure of a document + * whose first key is "$ref". + */ + bool check_special_dollar_keys; +} validation_params; + +/** + * @brief State for a validator. + */ +typedef struct { + /// The parameters that control validation behavior + const validation_params *params; + /// Error storage that is updated if any validation encounters an error + bson_error_t error; + /// The zero-based index of the byte where validation stopped in case of an error. + size_t error_offset; +} validator; + +// Undef these macros, if they are defined. +#ifdef require_with_error +#undef require_with_error +#endif +#ifdef require +#undef require +#endif +#ifdef require_advance +#undef require_advance +#endif + +/** + * @brief Check that the given condition is satisfied, or set an error and return `false` + * + * @param Condition The condition that should evaluate to `true` + * @param Offset The byte offset where an error should be indicated. + * @param Code The error code that should be set if the condition fails + * @param ... The error string and format arguments to be used in the error message + * + * This macro assumes a `validator* self` is in scope. This macro will evaluate `return false` + * if the given condition is not true. + */ +#define require_with_error(Condition, Offset, Code, ...) \ + if (!(Condition)) { \ + self->error_offset = (Offset); \ + bson_set_error(&self->error, BSON_ERROR_INVALID, Code, __VA_ARGS__); \ + return false; \ + } else \ + ((void)0) + +/** + * @brief Check that the given condition is satisfied, or `return false` immediately. + * + * This macro does not modify the validator state. It only does an early-return. + */ +#define require(Cond) \ + if (!(Cond)) { \ + return false; \ + } else \ + ((void)0) + +/** + * @brief Advance the pointed-to iterator, check for errors, and test whether we are done. + * + * @param DoneVar An l-value of type `bool` that is set to `true` if the iterator hit the end of + * the document, otherwise `false` + * @param IteratorPointer An expression of type `bson_iter_t*`, which will be advanced. + * + * If advancing the iterator results in a decoding error, then this macro sets an error + * on the `validator* self` that is in scope and will immediately `return false`. + */ +#define require_advance(DoneVar, IteratorPointer) \ + if ((DoneVar = !bson_iter_next(IteratorPointer))) { \ + /* The iterator indicates that it stopped */ \ + if ((IteratorPointer)->err_off) { \ + /* The iterator stopped because of a decoding error */ \ + require_with_error(false, (IteratorPointer)->err_off, BSON_VALIDATE_CORRUPT, "corrupt BSON"); \ + } \ + } else \ + ((void)0) + +// Test if the element's key is equal to the given string +static bool +_key_is(bson_iter_t const *iter, const char *const key) +{ + BSON_ASSERT_PARAM(iter); + BSON_ASSERT_PARAM(key); + return !strcmp(bson_iter_key(iter), key); +} + +/** + * @brief Validate a document or array object, recursively. + * + * @param self The validator which will be updated and used to do the validation + * @param bson The object to be validated + * @param depth The validation depth. We indicate an error if this exceeds a limit. + * @return true If the object is valid + * @return false Otherwise + */ +static bool +_validate_doc(validator *self, const bson_t *bson, int depth); + +/** + * @brief Validate a UTF-8 string, if-and-only-if UTF-8 validation is requested + * + * @param self Pointer to the validator object + * @param offset The byte-offset of the string, used to set the error offset + * @param u8 Pointer to the first byte in a UTF-8 string + * @param u8len The length of the array pointed-to by `u8` + * @return true If the UTF-8 string is valid, or if UTF-8 validation is disabled + * @return false If UTF-8 validation is requested, AND (the UTF-8 string is invalid OR (UTF-8 strings should not contain + * null characters and the UTF-8 string contains a null character)) + */ +static bool +_maybe_validate_utf8(validator *self, size_t offset, const char *u8, size_t u8len) +{ + BSON_ASSERT_PARAM(self); + BSON_ASSERT_PARAM(u8); + if (self->params->allow_invalid_utf8) { + // We are not doing UTF-8 checks, so always succeed + return true; + } + // Validate UTF-8 + const bool u8okay = bson_utf8_validate(u8, u8len, self->params->allow_null_in_utf8); + if (u8okay) { + // Valid UTF-8, no more checks + return true; + } + // Validation error. It may be invalid UTF-8, or it could be valid UTF-8 with a disallowed null + if (!self->params->allow_null_in_utf8) { + // We are disallowing null in UTF-8. Check whether it is invalid UTF-8, or is + // valid UTF-8 with a null character + const bool u8okay_with_null = bson_utf8_validate(u8, u8len, true); + if (u8okay_with_null) { + // The UTF-8 is valid, but contains a null character. + require_with_error( + false, offset, BSON_VALIDATE_UTF8_ALLOW_NULL, "UTF-8 string contains a U+0000 (null) character"); + } + } + // The UTF-8 is invalid, regardless of whether it contains a null character + require_with_error(false, offset, BSON_VALIDATE_UTF8, "Text element is not valid UTF-8"); +} + +// Same as `_maybe_validate_u8`, but relies on a null-terminated C string to get the string length +static bool +_maybe_validate_utf8_cstring(validator *self, size_t offset, const char *const u8) +{ + BSON_ASSERT_PARAM(self); + BSON_ASSERT_PARAM(u8); + return _maybe_validate_utf8(self, offset, u8, strlen(u8)); +} + +/** + * @brief Validate a string-like element (UTF-8, Symbol, or Code) + * + * This function relies on the representation of the text-like elements within + * the iterator struct to reduce code dup around text validation. + */ +static bool +_validate_stringlike_element(validator *self, bson_iter_t const *iter) +{ + BSON_ASSERT_PARAM(self); + BSON_ASSERT_PARAM(iter); + // iter->d1 is the offset to the string header. Subtract 1 to exclude the null terminator + const uint32_t u8len = mlib_read_u32le(iter->raw + iter->d1) - 1; + // iter->d2 is the offset to the first byte of the string + const char *u8 = (const char *)iter->raw + iter->d2; + return _maybe_validate_utf8(self, iter->off, u8, u8len); +} + +static bool +_validate_regex_elem(validator *self, bson_iter_t const *iter) +{ + BSON_ASSERT_PARAM(self); + BSON_ASSERT_PARAM(iter); + mlib_check(BSON_ITER_HOLDS_REGEX(iter)); + const char *opts; + const char *const rx = bson_iter_regex(iter, &opts); + mlib_check(rx); + mlib_check(opts); + return _maybe_validate_utf8_cstring(self, iter->off, rx) // + && _maybe_validate_utf8_cstring(self, iter->off, opts); +} + +static bool +_validate_codewscope_elem(validator *self, bson_iter_t const *iter, int depth) +{ + BSON_ASSERT_PARAM(self); + BSON_ASSERT_PARAM(iter); + mlib_check(BSON_ITER_HOLDS_CODEWSCOPE(iter)); + // Extract the code and the scope object + uint8_t const *doc; + uint32_t doc_len; + uint32_t u8len; + const char *const u8 = bson_iter_codewscope(iter, &u8len, &doc_len, &doc); + bson_t scope; + require_with_error( + bson_init_static(&scope, doc, doc_len), iter->off, BSON_VALIDATE_CORRUPT, "corrupt scope document"); + + // Validate the code string + require(_maybe_validate_utf8(self, iter->off, u8, u8len)); + + // Now we validate the scope object. + // Don't validate the scope document using the parent parameters, because it should + // be treated as an opaque closure of JS variables. + validation_params const scope_params = { + // JS object keys can contain dots + .allow_dot_in_keys = true, + // JS object keys can be empty + .allow_empty_keys = true, + // JS strings can contain null bytes + .allow_null_in_utf8 = true, + // JS strings need to encode properly + .allow_invalid_utf8 = false, + // JS allows object keys to have dollars + .check_special_dollar_keys = false, + }; + validator scope_validator = {.params = &scope_params}; + // We could do more validation that the scope keys are valid JS identifiers, + // but that would require using a full Unicode database. + if (_validate_doc(&scope_validator, &scope, depth)) { + // No error + return true; + } + // Validation error. Copy the error message, adding the name of the bad element + bson_set_error(&self->error, + scope_validator.error.domain, + scope_validator.error.code, + "Error in scope document for element \"%s\": %s", + bson_iter_key(iter), + scope_validator.error.message); + // Adjust the error offset by the offset of the iterator + self->error_offset = scope_validator.error_offset + iter->off; + return false; +} + +// Validate an element's key string according to the validation rules +static bool +_validate_element_key(validator *self, bson_iter_t const *iter) +{ + BSON_ASSERT_PARAM(self); + BSON_ASSERT_PARAM(iter); + + const char *const key = bson_iter_key(iter); + mlib_check(key); + const size_t key_len = bson_iter_key_len(iter); + + // Check the UTF-8 of the key + require(_maybe_validate_utf8(self, iter->off, key, key_len)); + + // Check for special keys + if (self->params->check_special_dollar_keys) { + // dollar-keys are checked during the startup of _validate_doc. If we get here, there's a problem. + require_with_error( + key[0] != '$', iter->off, BSON_VALIDATE_DOLLAR_KEYS, "Disallowed '$' in element key: \"%s\"", key); + } + + if (!self->params->allow_empty_keys) { + require_with_error(key_len != 0, iter->off, BSON_VALIDATE_EMPTY_KEYS, "Element key cannot be an empty string"); + } + + if (!self->params->allow_dot_in_keys) { + require_with_error( + !strstr(key, "."), iter->off, BSON_VALIDATE_DOT_KEYS, "Disallowed '.' in element key: \"%s\"", key); + } + + return true; +} + +// Extract a document referred-to by the given iterator. It must point to a +// document or array element. Returns `false` if `bson_init_static` returns false +static bool +_get_subdocument(bson_t *subdoc, bson_iter_t const *iter) +{ + BSON_ASSERT_PARAM(subdoc); + BSON_ASSERT_PARAM(iter); + uint32_t len = mlib_read_u32le(iter->raw + iter->d1); + uint8_t const *data = (uint8_t const *)iter->raw + iter->d1; + return bson_init_static(subdoc, data, len); +} + +// Validate the value of an element, without checking its key +static bool +_validate_element_value(validator *self, bson_iter_t const *iter, int depth) +{ + BSON_ASSERT_PARAM(self); + BSON_ASSERT_PARAM(iter); + + const bson_type_t type = bson_iter_type(iter); + switch (type) { + default: + case BSON_TYPE_EOD: + BSON_UNREACHABLE("Validation execution encountered an element of type 0x0, but this should not happen as tag " + "validation is handled before we get to this point."); + case BSON_TYPE_DOUBLE: + case BSON_TYPE_NULL: + case BSON_TYPE_OID: + case BSON_TYPE_INT32: + case BSON_TYPE_INT64: + case BSON_TYPE_MINKEY: + case BSON_TYPE_MAXKEY: + case BSON_TYPE_TIMESTAMP: + case BSON_TYPE_UNDEFINED: + case BSON_TYPE_DECIMAL128: + case BSON_TYPE_DATE_TIME: + case BSON_TYPE_BOOL: + // No validation on these simple scalar elements. `bson_iter_next` does validation + // on these objects for us. + return true; + case BSON_TYPE_BINARY: + // Note: BSON binary validation is handled by bson_iter_next, which checks the + // internal structure properly. If we get here, then the binary data is okay. + return true; + case BSON_TYPE_DBPOINTER: + // DBPointer contains more than just a string, but we only need to validate + // the string component, which happens to align with the repr of other stringlike + // elements. bson_iter_next will do the validation on the element's size. + //! fallthrough + case BSON_TYPE_SYMBOL: + case BSON_TYPE_CODE: + case BSON_TYPE_UTF8: + return _validate_stringlike_element(self, iter); + case BSON_TYPE_DOCUMENT: + case BSON_TYPE_ARRAY: { + bson_t doc; + require_with_error(_get_subdocument(&doc, iter), iter->off, BSON_VALIDATE_CORRUPT, "corrupt BSON"); + if (_validate_doc(self, &doc, depth)) { + // No error + return true; + } + // Error in subdocument. Adjust the error offset for the current iterator position, + // plus the key length, plus 2 for the tag and key's null terminator. + self->error_offset += iter->off + bson_iter_key_len(iter) + 2; + return false; + } + + case BSON_TYPE_REGEX: + return _validate_regex_elem(self, iter); + case BSON_TYPE_CODEWSCOPE: + return _validate_codewscope_elem(self, iter, depth); + } +} + +// Validate a single BSON element referred-to by the given iterator +static bool +_validate_element(validator *self, bson_iter_t *iter, int depth) +{ + BSON_ASSERT_PARAM(self); + BSON_ASSERT_PARAM(iter); + return _validate_element_key(self, iter) && _validate_element_value(self, iter, depth); +} + +/** + * @brief Validate the elements of a document, beginning with the element pointed-to + * by the given iterator. + */ +static bool +_validate_remaining_elements(validator *self, bson_iter_t *iter, int depth) +{ + BSON_ASSERT_PARAM(self); + BSON_ASSERT_PARAM(iter); + bool done = false; + while (!done) { + require(_validate_element(self, iter, depth)); + require_advance(done, iter); + } + return true; +} + +// Do validation for a DBRef document, indicated by a leading $ref key +static bool +_validate_dbref(validator *self, bson_iter_t *iter, int depth) +{ + BSON_ASSERT_PARAM(self); + BSON_ASSERT_PARAM(iter); + + // The iterator must be pointing to the initial $ref element + mlib_check(_key_is(iter, "$ref")); + // Check that $ref is a UTF-8 element + require_with_error( + BSON_ITER_HOLDS_UTF8(iter), iter->off, BSON_VALIDATE_DOLLAR_KEYS, "$ref element must be a UTF-8 element"); + require(_validate_element_value(self, iter, depth)); + + // We require an $id as the next element + bool done; + require_advance(done, iter); + require_with_error( + !done && _key_is(iter, "$id"), iter->off, BSON_VALIDATE_DOLLAR_KEYS, "Expected an $id element following $ref"); + // While $id is typically a OID value, it is not constraint to any specific type, so + // we just validate it as an arbitrary value. + require(_validate_element_value(self, iter, depth)); + + // We should stop, or we should have a $db, or we may have other elements + require_advance(done, iter); + if (done) { + // No more elements. Nothing left to check + return true; + } + // If it's a $db, check that it's a UTF-8 string + if (_key_is(iter, "$db")) { + require_with_error(BSON_ITER_HOLDS_UTF8(iter), + iter->off, + BSON_VALIDATE_DOLLAR_KEYS, + "$db element in DBRef must be a UTF-8 element"); + require(_validate_element_value(self, iter, depth)); + // Advance past the $db + require_advance(done, iter); + if (done) { + // Nothing left to do + return true; + } + } + // All subsequent elements should be validated as normal, and we don't expect + // any more $-keys + return _validate_remaining_elements(self, iter, depth); +} + +// If we are validating special $-keys, validate a document whose first element is a $-key +static bool +_validate_dollar_doc(validator *self, bson_iter_t *iter, int depth) +{ + BSON_ASSERT_PARAM(self); + BSON_ASSERT_PARAM(iter); + if (_key_is(iter, "$ref")) { + return _validate_dbref(self, iter, depth); + } + // Have the element key validator issue an error message about the bad $-key + bool okay = _validate_element_key(self, iter); + mlib_check(!okay); + return false; +} + +static bool +_validate_doc(validator *self, const bson_t *bson, int depth) +{ + BSON_ASSERT_PARAM(self); + BSON_ASSERT_PARAM(bson); + + require_with_error( + depth <= BSON_VALIDATION_MAX_NESTING_DEPTH, 0, BSON_VALIDATE_CORRUPT, "BSON document nesting depth is too deep"); + // We increment the depth here, otherwise we'd have `depth + 1` in several places. + ++depth; + + // Initialize an iterator into the document to be validated + bson_iter_t iter; + require_with_error( + bson_iter_init(&iter, bson), 0, BSON_VALIDATE_CORRUPT, "Document header corruption, unable to iterate"); + bool done; + require_advance(done, &iter); + if (done) { + // Nothing to check (empty doc/array) + return true; + } + + // Check if the first key starts with a dollar + if (self->params->check_special_dollar_keys) { + const char *const key = bson_iter_key(&iter); + if (key[0] == '$') { + return _validate_dollar_doc(self, &iter, depth); + } + } + + return _validate_remaining_elements(self, &iter, depth); +} + +// This private function is called by `bson_validate_with_error_and_offset` +bool +_bson_validate_impl_v2(const bson_t *bson, bson_validate_flags_t flags, size_t *offset, bson_error_t *error) +{ + BSON_ASSERT_PARAM(bson); + BSON_ASSERT_PARAM(offset); + BSON_ASSERT_PARAM(error); + + // Clear the error + *error = (bson_error_t){0}; + + // Initialize validation parameters + validation_params const params = { + .allow_invalid_utf8 = !(flags & BSON_VALIDATE_UTF8), + .allow_null_in_utf8 = flags & BSON_VALIDATE_UTF8_ALLOW_NULL, + .check_special_dollar_keys = (flags & BSON_VALIDATE_DOLLAR_KEYS), + .allow_dot_in_keys = !(flags & BSON_VALIDATE_DOT_KEYS), + .allow_empty_keys = !(flags & BSON_VALIDATE_EMPTY_KEYS), + }; + + // Start the validator on the root document + validator v = {.params = ¶ms}; + bool okay = _validate_doc(&v, bson, 0); + *offset = v.error_offset; + *error = v.error; + mlib_check(okay == (v.error.code == 0) && + "Validation routine should return `false` if-and-only-if it sets an error code"); + return okay; +} diff --git a/bsonjs/bson/bson-version.h b/bsonjs/bson/version.h similarity index 87% rename from bsonjs/bson/bson-version.h rename to bsonjs/bson/version.h index 1283924..6edacb7 100644 --- a/bsonjs/bson/bson-version.h +++ b/bsonjs/bson/version.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,6 @@ * limitations under the License. */ - -#if !defined(BSON_INSIDE) && !defined(BSON_COMPILATION) -#error "Only can be included directly." -#endif - // clang-format off #ifndef BSON_VERSION_H @@ -30,7 +25,7 @@ * * BSON major version component (e.g. 1 if %BSON_VERSION is 1.2.3) */ -#define BSON_MAJOR_VERSION (1) +#define BSON_MAJOR_VERSION (2) /** @@ -38,7 +33,7 @@ * * BSON minor version component (e.g. 2 if %BSON_VERSION is 1.2.3) */ -#define BSON_MINOR_VERSION (27) +#define BSON_MINOR_VERSION (5) /** @@ -46,7 +41,7 @@ * * BSON micro version component (e.g. 3 if %BSON_VERSION is 1.2.3) */ -#define BSON_MICRO_VERSION (2) +#define BSON_MICRO_VERSION (0) /** @@ -61,7 +56,7 @@ * * BSON version. */ -#define BSON_VERSION (1.27.2) +#define BSON_VERSION (2.5.0) /** @@ -70,7 +65,7 @@ * BSON version, encoded as a string, useful for printing and * concatenation. */ -#define BSON_VERSION_S "1.27.2" +#define BSON_VERSION_S "2.5.0" /** diff --git a/bsonjs/common/common-atomic-private.h b/bsonjs/common/common-atomic-private.h new file mode 100644 index 0000000..9623a06 --- /dev/null +++ b/bsonjs/common/common-atomic-private.h @@ -0,0 +1,657 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + + +#ifndef MONGO_C_DRIVER_COMMON_ATOMIC_PRIVATE_H +#define MONGO_C_DRIVER_COMMON_ATOMIC_PRIVATE_H + + +#include // BSON_INLINE + +#ifdef _MSC_VER +#include +#endif + + +enum mcommon_memory_order { + mcommon_memory_order_seq_cst, + mcommon_memory_order_acquire, + mcommon_memory_order_release, + mcommon_memory_order_relaxed, + mcommon_memory_order_acq_rel, + mcommon_memory_order_consume, +}; + +#if defined(_M_ARM) /* MSVC memorder atomics are only avail on ARM */ +#define MSVC_MEMORDER_SUFFIX(X) X +#else +#define MSVC_MEMORDER_SUFFIX(X) +#endif + +#if defined(USE_LEGACY_GCC_ATOMICS) || (!defined(__clang__) && __GNUC__ == 4) || defined(__xlC__) +#define MCOMMON_USE_LEGACY_GCC_ATOMICS +#else +#undef MCOMMON_USE_LEGACY_GCC_ATOMICS +#endif + +/* Not all GCC-like compilers support the current __atomic built-ins. Older + * GCC (pre-5) used different built-ins named with the __sync prefix. When + * compiling with such older GCC versions, it is necessary to use the applicable + * functions, which requires redefining BSON_IF_GNU_LIKE and defining the + * additional MCOMMON_IF_GNU_LEGACY_ATOMICS macro here. */ +#ifdef MCOMMON_USE_LEGACY_GCC_ATOMICS +#undef BSON_IF_GNU_LIKE +#define BSON_IF_GNU_LIKE(...) +#define MCOMMON_IF_GNU_LEGACY_ATOMICS(...) __VA_ARGS__ +#else +#define MCOMMON_IF_GNU_LEGACY_ATOMICS(...) +#endif + +/* CDRIVER-4229 zSeries with gcc 4.8.4 produces illegal instructions for int and + * int32 atomic intrinsics. */ +#if defined(__s390__) || defined(__s390x__) || defined(__zarch__) +#define MCOMMON_EMULATE_INT32 +#define MCOMMON_EMULATE_INT +#endif + +/* 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 MCOMMON_EMULATE_PTR +#endif + +#define DEF_ATOMIC_OP(MSVC_Intrinsic, GNU_Intrinsic, GNU_Legacy_Intrinsic, Order, ...) \ + do { \ + switch (Order) { \ + case mcommon_memory_order_acq_rel: \ + BSON_IF_MSVC(return MSVC_Intrinsic(__VA_ARGS__);) \ + BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_ACQ_REL);) \ + MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);) \ + case mcommon_memory_order_seq_cst: \ + BSON_IF_MSVC(return MSVC_Intrinsic(__VA_ARGS__);) \ + BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_SEQ_CST);) \ + MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);) \ + case mcommon_memory_order_acquire: \ + BSON_IF_MSVC(return BSON_CONCAT(MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX(_acq))(__VA_ARGS__);) \ + BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_ACQUIRE);) \ + MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);) \ + case mcommon_memory_order_consume: \ + BSON_IF_MSVC(return BSON_CONCAT(MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX(_acq))(__VA_ARGS__);) \ + BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_CONSUME);) \ + MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);) \ + case mcommon_memory_order_release: \ + BSON_IF_MSVC(return BSON_CONCAT(MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX(_rel))(__VA_ARGS__);) \ + BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_RELEASE);) \ + MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);) \ + case mcommon_memory_order_relaxed: \ + BSON_IF_MSVC(return BSON_CONCAT(MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX(_nf))(__VA_ARGS__);) \ + BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_RELAXED);) \ + MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);) \ + default: \ + BSON_UNREACHABLE("Invalid mcommon_memory_order value"); \ + } \ + } 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);) \ + MCOMMON_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);) \ + MCOMMON_IF_GNU_LEGACY_ATOMICS(__typeof__(ExpectActualVar) _val; \ + _val = __sync_val_compare_and_swap(Ptr, ExpectActualVar, NewValue); \ + ExpectActualVar = _val;) \ + } while (0) + + +// on Windows, when invoking the appropriate intrinsic, cast to the type the intrinsic is declared with to avoid +// warnings irrespective of the signedness of Type; all these operations are bit-level and don't care about the sign +#define DECL_ATOMIC_INTEGRAL(NamePart, Type, VCIntrinType, VCIntrinSuffix) \ + static BSON_INLINE Type mcommon_atomic_##NamePart##_fetch_add( \ + Type volatile *a, Type addend, enum mcommon_memory_order ord) \ + { \ + DEF_ATOMIC_OP(BSON_CONCAT(_InterlockedExchangeAdd, VCIntrinSuffix), \ + __atomic_fetch_add, \ + __sync_fetch_and_add, \ + ord, \ + BSON_IF_MSVC((volatile VCIntrinType *)) a, \ + addend); \ + } \ + \ + static BSON_INLINE Type mcommon_atomic_##NamePart##_fetch_sub( \ + Type volatile *a, Type subtrahend, enum mcommon_memory_order ord) \ + { \ + /* MSVC doesn't have a subtract intrinsic, so just reuse addition */ \ + BSON_IF_MSVC(return mcommon_atomic_##NamePart##_fetch_add(a, -subtrahend, ord);) \ + BSON_IF_GNU_LIKE(DEF_ATOMIC_OP(~, __atomic_fetch_sub, ~, ord, a, subtrahend);) \ + MCOMMON_IF_GNU_LEGACY_ATOMICS(DEF_ATOMIC_OP(~, ~, __sync_fetch_and_sub, ord, a, subtrahend);) \ + } \ + \ + static BSON_INLINE Type mcommon_atomic_##NamePart##_fetch(Type volatile const *a, enum mcommon_memory_order order) \ + { \ + /* MSVC doesn't have a load intrinsic, so just add zero */ \ + BSON_IF_MSVC(return mcommon_atomic_##NamePart##_fetch_add((Type volatile *)a, 0, order);) \ + /* GNU doesn't want RELEASE order for the fetch operation, so we can't \ + * just use DEF_ATOMIC_OP. */ \ + BSON_IF_GNU_LIKE(switch (order) { \ + case mcommon_memory_order_release: /* Fall back to seqcst */ \ + case mcommon_memory_order_acq_rel: /* Fall back to seqcst */ \ + case mcommon_memory_order_seq_cst: \ + return __atomic_load_n(a, __ATOMIC_SEQ_CST); \ + case mcommon_memory_order_acquire: \ + return __atomic_load_n(a, __ATOMIC_ACQUIRE); \ + case mcommon_memory_order_consume: \ + return __atomic_load_n(a, __ATOMIC_CONSUME); \ + case mcommon_memory_order_relaxed: \ + return __atomic_load_n(a, __ATOMIC_RELAXED); \ + default: \ + BSON_UNREACHABLE("Invalid mcommon_memory_order value"); \ + }) \ + MCOMMON_IF_GNU_LEGACY_ATOMICS({ \ + BSON_UNUSED(order); \ + __sync_synchronize(); \ + return *a; \ + }) \ + } \ + \ + static BSON_INLINE Type mcommon_atomic_##NamePart##_exchange( \ + Type volatile *a, Type value, enum mcommon_memory_order ord) \ + { \ + BSON_IF_MSVC(DEF_ATOMIC_OP(BSON_CONCAT(_InterlockedExchange, VCIntrinSuffix), \ + ~, \ + ~, \ + ord, \ + BSON_IF_MSVC((volatile VCIntrinType *)) a, \ + value);) \ + /* GNU doesn't want CONSUME order for the exchange operation, so we \ + * cannot use DEF_ATOMIC_OP. */ \ + BSON_IF_GNU_LIKE(switch (ord) { \ + case mcommon_memory_order_acq_rel: \ + return __atomic_exchange_n(a, value, __ATOMIC_ACQ_REL); \ + case mcommon_memory_order_release: \ + return __atomic_exchange_n(a, value, __ATOMIC_RELEASE); \ + case mcommon_memory_order_seq_cst: \ + return __atomic_exchange_n(a, value, __ATOMIC_SEQ_CST); \ + case mcommon_memory_order_consume: /* Fall back to acquire */ \ + case mcommon_memory_order_acquire: \ + return __atomic_exchange_n(a, value, __ATOMIC_ACQUIRE); \ + case mcommon_memory_order_relaxed: \ + return __atomic_exchange_n(a, value, __ATOMIC_RELAXED); \ + default: \ + BSON_UNREACHABLE("Invalid mcommon_memory_order value"); \ + }) \ + MCOMMON_IF_GNU_LEGACY_ATOMICS(BSON_UNUSED(ord); return __sync_val_compare_and_swap(a, *a, value);) \ + } \ + \ + static BSON_INLINE Type mcommon_atomic_##NamePart##_compare_exchange_strong( \ + Type volatile *a, Type expect, Type new_value, enum mcommon_memory_order ord) \ + { \ + Type actual = expect; \ + switch (ord) { \ + case mcommon_memory_order_release: \ + case mcommon_memory_order_acq_rel: \ + case mcommon_memory_order_seq_cst: \ + DEF_ATOMIC_CMPEXCH_STRONG( \ + VCIntrinSuffix, , __ATOMIC_SEQ_CST, BSON_IF_MSVC((volatile VCIntrinType *)) a, actual, new_value); \ + break; \ + case mcommon_memory_order_acquire: \ + DEF_ATOMIC_CMPEXCH_STRONG(VCIntrinSuffix, \ + MSVC_MEMORDER_SUFFIX(_acq), \ + __ATOMIC_ACQUIRE, \ + BSON_IF_MSVC((volatile VCIntrinType *)) a, \ + actual, \ + new_value); \ + break; \ + case mcommon_memory_order_consume: \ + DEF_ATOMIC_CMPEXCH_STRONG(VCIntrinSuffix, \ + MSVC_MEMORDER_SUFFIX(_acq), \ + __ATOMIC_CONSUME, \ + BSON_IF_MSVC((volatile VCIntrinType *)) a, \ + actual, \ + new_value); \ + break; \ + case mcommon_memory_order_relaxed: \ + DEF_ATOMIC_CMPEXCH_STRONG(VCIntrinSuffix, \ + MSVC_MEMORDER_SUFFIX(_nf), \ + __ATOMIC_RELAXED, \ + BSON_IF_MSVC((volatile VCIntrinType *)) a, \ + actual, \ + new_value); \ + break; \ + default: \ + BSON_UNREACHABLE("Invalid mcommon_memory_order value"); \ + } \ + return actual; \ + } \ + \ + static BSON_INLINE Type mcommon_atomic_##NamePart##_compare_exchange_weak( \ + Type volatile *a, Type expect, Type new_value, enum mcommon_memory_order ord) \ + { \ + Type actual = expect; \ + switch (ord) { \ + case mcommon_memory_order_release: \ + case mcommon_memory_order_acq_rel: \ + case mcommon_memory_order_seq_cst: \ + DEF_ATOMIC_CMPEXCH_WEAK( \ + VCIntrinSuffix, , __ATOMIC_SEQ_CST, BSON_IF_MSVC((volatile VCIntrinType *)) a, actual, new_value); \ + break; \ + case mcommon_memory_order_acquire: \ + DEF_ATOMIC_CMPEXCH_WEAK(VCIntrinSuffix, \ + MSVC_MEMORDER_SUFFIX(_acq), \ + __ATOMIC_ACQUIRE, \ + BSON_IF_MSVC((volatile VCIntrinType *)) a, \ + actual, \ + new_value); \ + break; \ + case mcommon_memory_order_consume: \ + DEF_ATOMIC_CMPEXCH_WEAK(VCIntrinSuffix, \ + MSVC_MEMORDER_SUFFIX(_acq), \ + __ATOMIC_CONSUME, \ + BSON_IF_MSVC((volatile VCIntrinType *)) a, \ + actual, \ + new_value); \ + break; \ + case mcommon_memory_order_relaxed: \ + DEF_ATOMIC_CMPEXCH_WEAK(VCIntrinSuffix, \ + MSVC_MEMORDER_SUFFIX(_nf), \ + __ATOMIC_RELAXED, \ + BSON_IF_MSVC((volatile VCIntrinType *)) a, \ + actual, \ + new_value); \ + break; \ + default: \ + BSON_UNREACHABLE("Invalid mcommon_memory_order value"); \ + } \ + return actual; \ + } + +#define DECL_ATOMIC_STDINT(Name, VCIntrinType, VCSuffix) DECL_ATOMIC_INTEGRAL(Name, Name##_t, VCIntrinType, VCSuffix) + +#if defined(_MSC_VER) || defined(MCOMMON_USE_LEGACY_GCC_ATOMICS) +/* MSVC and GCC require built-in types (not typedefs) for their atomic + * intrinsics. + * When using clang-cl it pretends to be MSVC (by defining _MSC_VER) + * but then complains about int8_t actual arguments (signed char on Windows) vs char formal arguments mismatch + */ +#if defined(_MSC_VER) && !defined(__clang__) +#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, char, 8) +DECL_ATOMIC_INTEGRAL(int16, short, short, 16) +#if !defined(MCOMMON_EMULATE_INT32) +DECL_ATOMIC_INTEGRAL(int32, DECL_ATOMIC_INTEGRAL_INT32, long, ) +#endif +#if !defined(MCOMMON_EMULATE_INT) +DECL_ATOMIC_INTEGRAL(int, DECL_ATOMIC_INTEGRAL_INT, long, ) +#endif +#else +/* Other compilers that we support provide generic intrinsics */ +DECL_ATOMIC_STDINT(int8, char, 8) +DECL_ATOMIC_STDINT(int16, short, 16) +#if !defined(MCOMMON_EMULATE_INT32) +DECL_ATOMIC_STDINT(int32, long, ) +#endif +#if !defined(MCOMMON_EMULATE_INT) +DECL_ATOMIC_INTEGRAL(int, int, long, ) +#endif +#endif + +#ifndef DECL_ATOMIC_INTEGRAL_INT32 +#define DECL_ATOMIC_INTEGRAL_INT32 int32_t +#endif + +#define _mcommon_emul_atomic_int64_fetch_add COMMON_NAME(emul_atomic_int64_fetch_add) +#define _mcommon_emul_atomic_int64_exchange COMMON_NAME(emul_atomic_int64_exchange) +#define _mcommon_emul_atomic_int64_compare_exchange_strong COMMON_NAME(emul_atomic_int64_compare_exchange_strong) +#define _mcommon_emul_atomic_int64_compare_exchange_weak COMMON_NAME(emul_atomic_int64_compare_exchange_weak) +#define _mcommon_emul_atomic_int32_fetch_add COMMON_NAME(emul_atomic_int32_fetch_add) +#define _mcommon_emul_atomic_int32_exchange COMMON_NAME(emul_atomic_int32_exchange) +#define _mcommon_emul_atomic_int32_compare_exchange_strong COMMON_NAME(emul_atomic_int32_compare_exchange_strong) +#define _mcommon_emul_atomic_int32_compare_exchange_weak COMMON_NAME(emul_atomic_int32_compare_exchange_weak) +#define _mcommon_emul_atomic_int_fetch_add COMMON_NAME(emul_atomic_int_fetch_add) +#define _mcommon_emul_atomic_int_exchange COMMON_NAME(emul_atomic_int_exchange) +#define _mcommon_emul_atomic_int_compare_exchange_strong COMMON_NAME(emul_atomic_int_compare_exchange_strong) +#define _mcommon_emul_atomic_int_compare_exchange_weak COMMON_NAME(emul_atomic_int_compare_exchange_weak) +#define _mcommon_emul_atomic_ptr_exchange COMMON_NAME(emul_atomic_ptr_exchange) +#define mcommon_thrd_yield COMMON_NAME(thrd_yield) + +int64_t +_mcommon_emul_atomic_int64_fetch_add(int64_t volatile *val, int64_t v, enum mcommon_memory_order); +int64_t +_mcommon_emul_atomic_int64_exchange(int64_t volatile *val, int64_t v, enum mcommon_memory_order); +int64_t +_mcommon_emul_atomic_int64_compare_exchange_strong(int64_t volatile *val, + int64_t expect_value, + int64_t new_value, + enum mcommon_memory_order); + +int64_t +_mcommon_emul_atomic_int64_compare_exchange_weak(int64_t volatile *val, + int64_t expect_value, + int64_t new_value, + enum mcommon_memory_order); + +int32_t +_mcommon_emul_atomic_int32_fetch_add(int32_t volatile *val, int32_t v, enum mcommon_memory_order); +int32_t +_mcommon_emul_atomic_int32_exchange(int32_t volatile *val, int32_t v, enum mcommon_memory_order); +int32_t +_mcommon_emul_atomic_int32_compare_exchange_strong(int32_t volatile *val, + int32_t expect_value, + int32_t new_value, + enum mcommon_memory_order); + +int32_t +_mcommon_emul_atomic_int32_compare_exchange_weak(int32_t volatile *val, + int32_t expect_value, + int32_t new_value, + enum mcommon_memory_order); + +int +_mcommon_emul_atomic_int_fetch_add(int volatile *val, int v, enum mcommon_memory_order); +int +_mcommon_emul_atomic_int_exchange(int volatile *val, int v, enum mcommon_memory_order); +int +_mcommon_emul_atomic_int_compare_exchange_strong(int volatile *val, + int expect_value, + int new_value, + enum mcommon_memory_order); + +int +_mcommon_emul_atomic_int_compare_exchange_weak(int volatile *val, + int expect_value, + int new_value, + enum mcommon_memory_order); + +void * +_mcommon_emul_atomic_ptr_exchange(void *volatile *val, void *v, enum mcommon_memory_order); + +void +mcommon_thrd_yield(void); + +#if (defined(_MSC_VER) && !defined(_M_IX86)) || (defined(__LP64__) && __LP64__) +/* (64-bit intrinsics are only available in x64) */ +#ifdef _MSC_VER +DECL_ATOMIC_INTEGRAL(int64, __int64, __int64, 64) +#else +DECL_ATOMIC_STDINT(int64, __int64, 64) +#endif +#else +static BSON_INLINE int64_t +mcommon_atomic_int64_fetch(const int64_t volatile *val, enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int64_fetch_add((int64_t volatile *)val, 0, order); +} + +static BSON_INLINE int64_t +mcommon_atomic_int64_fetch_add(int64_t volatile *val, int64_t v, enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int64_fetch_add(val, v, order); +} + +static BSON_INLINE int64_t +mcommon_atomic_int64_fetch_sub(int64_t volatile *val, int64_t v, enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int64_fetch_add(val, -v, order); +} + +static BSON_INLINE int64_t +mcommon_atomic_int64_exchange(int64_t volatile *val, int64_t v, enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int64_exchange(val, v, order); +} + +static BSON_INLINE int64_t +mcommon_atomic_int64_compare_exchange_strong(int64_t volatile *val, + int64_t expect_value, + int64_t new_value, + enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int64_compare_exchange_strong(val, expect_value, new_value, order); +} + +static BSON_INLINE int64_t +mcommon_atomic_int64_compare_exchange_weak(int64_t volatile *val, + int64_t expect_value, + int64_t new_value, + enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int64_compare_exchange_weak(val, expect_value, new_value, order); +} +#endif + +#if defined(MCOMMON_EMULATE_INT32) +static BSON_INLINE int32_t +mcommon_atomic_int32_fetch(const int32_t volatile *val, enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int32_fetch_add((int32_t volatile *)val, 0, order); +} + +static BSON_INLINE int32_t +mcommon_atomic_int32_fetch_add(int32_t volatile *val, int32_t v, enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int32_fetch_add(val, v, order); +} + +static BSON_INLINE int32_t +mcommon_atomic_int32_fetch_sub(int32_t volatile *val, int32_t v, enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int32_fetch_add(val, -v, order); +} + +static BSON_INLINE int32_t +mcommon_atomic_int32_exchange(int32_t volatile *val, int32_t v, enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int32_exchange(val, v, order); +} + +static BSON_INLINE int32_t +mcommon_atomic_int32_compare_exchange_strong(int32_t volatile *val, + int32_t expect_value, + int32_t new_value, + enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int32_compare_exchange_strong(val, expect_value, new_value, order); +} + +static BSON_INLINE int32_t +mcommon_atomic_int32_compare_exchange_weak(int32_t volatile *val, + int32_t expect_value, + int32_t new_value, + enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int32_compare_exchange_weak(val, expect_value, new_value, order); +} +#endif /* MCOMMON_EMULATE_INT32 */ + +#if defined(MCOMMON_EMULATE_INT) +static BSON_INLINE int +mcommon_atomic_int_fetch(const int volatile *val, enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int_fetch_add((int volatile *)val, 0, order); +} + +static BSON_INLINE int +mcommon_atomic_int_fetch_add(int volatile *val, int v, enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int_fetch_add(val, v, order); +} + +static BSON_INLINE int +mcommon_atomic_int_fetch_sub(int volatile *val, int v, enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int_fetch_add(val, -v, order); +} + +static BSON_INLINE int +mcommon_atomic_int_exchange(int volatile *val, int v, enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int_exchange(val, v, order); +} + +static BSON_INLINE int +mcommon_atomic_int_compare_exchange_strong(int volatile *val, + int expect_value, + int new_value, + enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int_compare_exchange_strong(val, expect_value, new_value, order); +} + +static BSON_INLINE int +mcommon_atomic_int_compare_exchange_weak(int volatile *val, + int expect_value, + int new_value, + enum mcommon_memory_order order) +{ + return _mcommon_emul_atomic_int_compare_exchange_weak(val, expect_value, new_value, order); +} +#endif /* MCOMMON_EMULATE_INT */ + +static BSON_INLINE void * +mcommon_atomic_ptr_exchange(void *volatile *ptr, void *new_value, enum mcommon_memory_order ord) +{ +#if defined(MCOMMON_EMULATE_PTR) + return _mcommon_emul_atomic_ptr_exchange(ptr, new_value, ord); +#elif defined(MCOMMON_USE_LEGACY_GCC_ATOMICS) + /* The older __sync_val_compare_and_swap also takes oldval */ + DEF_ATOMIC_OP(_InterlockedExchangePointer, , __sync_val_compare_and_swap, ord, ptr, *ptr, new_value); +#else + DEF_ATOMIC_OP(_InterlockedExchangePointer, __atomic_exchange_n, , ord, ptr, new_value); +#endif +} + +static BSON_INLINE void * +mcommon_atomic_ptr_compare_exchange_strong(void *volatile *ptr, + void *expect, + void *new_value, + enum mcommon_memory_order ord) +{ + switch (ord) { + case mcommon_memory_order_release: + case mcommon_memory_order_acq_rel: + case mcommon_memory_order_seq_cst: + DEF_ATOMIC_CMPEXCH_STRONG(Pointer, , __ATOMIC_SEQ_CST, ptr, expect, new_value); + return expect; + case mcommon_memory_order_relaxed: + DEF_ATOMIC_CMPEXCH_STRONG(Pointer, MSVC_MEMORDER_SUFFIX(_nf), __ATOMIC_RELAXED, ptr, expect, new_value); + return expect; + case mcommon_memory_order_consume: + DEF_ATOMIC_CMPEXCH_STRONG(Pointer, MSVC_MEMORDER_SUFFIX(_acq), __ATOMIC_CONSUME, ptr, expect, new_value); + return expect; + case mcommon_memory_order_acquire: + DEF_ATOMIC_CMPEXCH_STRONG(Pointer, MSVC_MEMORDER_SUFFIX(_acq), __ATOMIC_ACQUIRE, ptr, expect, new_value); + return expect; + default: + BSON_UNREACHABLE("Invalid mcommon_memory_order value"); + } +} + + +static BSON_INLINE void * +mcommon_atomic_ptr_compare_exchange_weak(void *volatile *ptr, + void *expect, + void *new_value, + enum mcommon_memory_order ord) +{ + switch (ord) { + case mcommon_memory_order_release: + case mcommon_memory_order_acq_rel: + case mcommon_memory_order_seq_cst: + DEF_ATOMIC_CMPEXCH_WEAK(Pointer, , __ATOMIC_SEQ_CST, ptr, expect, new_value); + return expect; + case mcommon_memory_order_relaxed: + DEF_ATOMIC_CMPEXCH_WEAK(Pointer, MSVC_MEMORDER_SUFFIX(_nf), __ATOMIC_RELAXED, ptr, expect, new_value); + return expect; + case mcommon_memory_order_consume: + DEF_ATOMIC_CMPEXCH_WEAK(Pointer, MSVC_MEMORDER_SUFFIX(_acq), __ATOMIC_CONSUME, ptr, expect, new_value); + return expect; + case mcommon_memory_order_acquire: + DEF_ATOMIC_CMPEXCH_WEAK(Pointer, MSVC_MEMORDER_SUFFIX(_acq), __ATOMIC_ACQUIRE, ptr, expect, new_value); + return expect; + default: + BSON_UNREACHABLE("Invalid mcommon_memory_order value"); + } +} + + +static BSON_INLINE void * +mcommon_atomic_ptr_fetch(void *volatile const *ptr, enum mcommon_memory_order ord) +{ + return mcommon_atomic_ptr_compare_exchange_strong((void *volatile *)ptr, NULL, NULL, ord); +} + +#undef DECL_ATOMIC_STDINT +#undef DECL_ATOMIC_INTEGRAL +#undef DEF_ATOMIC_OP +#undef DEF_ATOMIC_CMPEXCH_STRONG +#undef DEF_ATOMIC_CMPEXCH_WEAK +#undef MSVC_MEMORDER_SUFFIX + +/** + * @brief Generate a full-fence memory barrier at the call site. + */ +static BSON_INLINE void +mcommon_atomic_thread_fence(void) +{ + BSON_IF_MSVC(MemoryBarrier();) + BSON_IF_GNU_LIKE(__sync_synchronize();) + MCOMMON_IF_GNU_LEGACY_ATOMICS(__sync_synchronize();) +} + +#ifdef MCOMMON_USE_LEGACY_GCC_ATOMICS +#undef BSON_IF_GNU_LIKE +#define BSON_IF_GNU_LIKE(...) __VA_ARGS__ +#endif +#undef MCOMMON_IF_GNU_LEGACY_ATOMICS +#undef MCOMMON_USE_LEGACY_GCC_ATOMICS + + +#undef MCOMMON_EMULATE_PTR +#undef MCOMMON_EMULATE_INT32 +#undef MCOMMON_EMULATE_INT + + +#endif /* MONGO_C_DRIVER_COMMON_ATOMIC_PRIVATE_H */ diff --git a/bsonjs/common/common-atomic.c b/bsonjs/common/common-atomic.c new file mode 100644 index 0000000..0b7fed5 --- /dev/null +++ b/bsonjs/common/common-atomic.c @@ -0,0 +1,251 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +#ifdef BSON_OS_UNIX +/* For sched_yield() */ +#include +#endif + +void +mcommon_thrd_yield(void) +{ + BSON_IF_WINDOWS(SwitchToThread();) + BSON_IF_POSIX(sched_yield();) +} + +/** + * Some platforms do not support compiler intrinsics for atomic operations. + * We emulate that here using a spin lock and regular arithmetic operations + */ +static int8_t gEmulAtomicLock = 0; + +static void +_lock_emul_atomic(void) +{ + int i; + if (mcommon_atomic_int8_compare_exchange_weak(&gEmulAtomicLock, 0, 1, mcommon_memory_order_acquire) == 0) { + /* Successfully took the spinlock */ + return; + } + /* Failed. Try taking ten more times, then begin sleeping. */ + for (i = 0; i < 10; ++i) { + if (mcommon_atomic_int8_compare_exchange_weak(&gEmulAtomicLock, 0, 1, mcommon_memory_order_acquire) == 0) { + /* Succeeded in taking the lock */ + return; + } + } + /* Still don't have the lock. Spin and yield */ + while (mcommon_atomic_int8_compare_exchange_weak(&gEmulAtomicLock, 0, 1, mcommon_memory_order_acquire) != 0) { + mcommon_thrd_yield(); + } +} + +static void +_unlock_emul_atomic(void) +{ + int64_t rv = mcommon_atomic_int8_exchange(&gEmulAtomicLock, 0, mcommon_memory_order_release); + BSON_ASSERT(rv == 1 && "Released atomic lock while not holding it"); +} + +int64_t +_mcommon_emul_atomic_int64_fetch_add(volatile int64_t *p, int64_t n, enum mcommon_memory_order _unused) +{ + int64_t ret; + + BSON_UNUSED(_unused); + + _lock_emul_atomic(); + ret = *p; + *p += n; + _unlock_emul_atomic(); + return ret; +} + +int64_t +_mcommon_emul_atomic_int64_exchange(volatile int64_t *p, int64_t n, enum mcommon_memory_order _unused) +{ + int64_t ret; + + BSON_UNUSED(_unused); + + _lock_emul_atomic(); + ret = *p; + *p = n; + _unlock_emul_atomic(); + return ret; +} + +int64_t +_mcommon_emul_atomic_int64_compare_exchange_strong(volatile int64_t *p, + int64_t expect_value, + int64_t new_value, + enum mcommon_memory_order _unused) +{ + int64_t ret; + + BSON_UNUSED(_unused); + + _lock_emul_atomic(); + ret = *p; + if (ret == expect_value) { + *p = new_value; + } + _unlock_emul_atomic(); + return ret; +} + +int64_t +_mcommon_emul_atomic_int64_compare_exchange_weak(volatile int64_t *p, + int64_t expect_value, + int64_t new_value, + enum mcommon_memory_order order) +{ + /* We're emulating. We can't do a weak version. */ + return _mcommon_emul_atomic_int64_compare_exchange_strong(p, expect_value, new_value, order); +} + + +int32_t +_mcommon_emul_atomic_int32_fetch_add(volatile int32_t *p, int32_t n, enum mcommon_memory_order _unused) +{ + int32_t ret; + + BSON_UNUSED(_unused); + + _lock_emul_atomic(); + ret = *p; + *p += n; + _unlock_emul_atomic(); + return ret; +} + +int32_t +_mcommon_emul_atomic_int32_exchange(volatile int32_t *p, int32_t n, enum mcommon_memory_order _unused) +{ + int32_t ret; + + BSON_UNUSED(_unused); + + _lock_emul_atomic(); + ret = *p; + *p = n; + _unlock_emul_atomic(); + return ret; +} + +int32_t +_mcommon_emul_atomic_int32_compare_exchange_strong(volatile int32_t *p, + int32_t expect_value, + int32_t new_value, + enum mcommon_memory_order _unused) +{ + int32_t ret; + + BSON_UNUSED(_unused); + + _lock_emul_atomic(); + ret = *p; + if (ret == expect_value) { + *p = new_value; + } + _unlock_emul_atomic(); + return ret; +} + +int32_t +_mcommon_emul_atomic_int32_compare_exchange_weak(volatile int32_t *p, + int32_t expect_value, + int32_t new_value, + enum mcommon_memory_order order) +{ + /* We're emulating. We can't do a weak version. */ + return _mcommon_emul_atomic_int32_compare_exchange_strong(p, expect_value, new_value, order); +} + + +int +_mcommon_emul_atomic_int_fetch_add(volatile int *p, int n, enum mcommon_memory_order _unused) +{ + int ret; + + BSON_UNUSED(_unused); + + _lock_emul_atomic(); + ret = *p; + *p += n; + _unlock_emul_atomic(); + return ret; +} + +int +_mcommon_emul_atomic_int_exchange(volatile int *p, int n, enum mcommon_memory_order _unused) +{ + int ret; + + BSON_UNUSED(_unused); + + _lock_emul_atomic(); + ret = *p; + *p = n; + _unlock_emul_atomic(); + return ret; +} + +int +_mcommon_emul_atomic_int_compare_exchange_strong(volatile int *p, + int expect_value, + int new_value, + enum mcommon_memory_order _unused) +{ + int ret; + + BSON_UNUSED(_unused); + + _lock_emul_atomic(); + ret = *p; + if (ret == expect_value) { + *p = new_value; + } + _unlock_emul_atomic(); + return ret; +} + +int +_mcommon_emul_atomic_int_compare_exchange_weak(volatile int *p, + int expect_value, + int new_value, + enum mcommon_memory_order order) +{ + /* We're emulating. We can't do a weak version. */ + return _mcommon_emul_atomic_int_compare_exchange_strong(p, expect_value, new_value, order); +} + +void * +_mcommon_emul_atomic_ptr_exchange(void *volatile *p, void *n, enum mcommon_memory_order _unused) +{ + void *ret; + + BSON_UNUSED(_unused); + + _lock_emul_atomic(); + ret = *p; + *p = n; + _unlock_emul_atomic(); + return ret; +} diff --git a/bsonjs/common/common-b64-private.h b/bsonjs/common/common-b64-private.h index 7f5c4e3..9561774 100644 --- a/bsonjs/common/common-b64-private.h +++ b/bsonjs/common/common-b64-private.h @@ -1,5 +1,5 @@ /* - * Copyright 2018-present MongoDB Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,30 +14,30 @@ * limitations under the License. */ -#include "common-prelude.h" +#include -#ifndef COMMON_B64_PRIVATE_H -#define COMMON_B64_PRIVATE_H +#ifndef MONGO_C_DRIVER_COMMON_B64_PRIVATE_H +#define MONGO_C_DRIVER_COMMON_B64_PRIVATE_H #include -#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) +#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 -mcommon_b64_ntop_calculate_target_size (size_t raw_size); +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 b64_pton (src, NULL, 0), which will read the src * data and return an exact size. */ size_t -mcommon_b64_pton_calculate_target_size (size_t base64_encoded_size); +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. @@ -45,7 +45,7 @@ mcommon_b64_pton_calculate_target_size (size_t base64_encoded_size); * hence the obscure name "ntop". */ int -mcommon_b64_ntop (uint8_t const *src, size_t srclength, char *target, size_t targsize); +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 @@ -53,6 +53,6 @@ mcommon_b64_ntop (uint8_t const *src, size_t srclength, char *target, size_t tar * encoded) to "network" (raw data), hence the obscure name "pton". */ int -mcommon_b64_pton (char const *src, uint8_t *target, size_t targsize); +mcommon_b64_pton(char const *src, uint8_t *target, size_t targsize); -#endif /* COMMON_B64_PRIVATE_H */ +#endif /* MONGO_C_DRIVER_COMMON_B64_PRIVATE_H */ diff --git a/bsonjs/common/common-b64.c b/bsonjs/common/common-b64.c index 051ca64..e0cc21a 100644 --- a/bsonjs/common/common-b64.c +++ b/bsonjs/common/common-b64.c @@ -40,12 +40,17 @@ * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ -#include "bson/bson.h" -#include "common-b64-private.h" +#include +#include + +#include + +#include +#include #define Assert(Cond) \ if (!(Cond)) \ - abort () + abort() static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char Pad64 = '='; @@ -113,31 +118,31 @@ static const char Pad64 = '='; */ int -mcommon_b64_ntop (uint8_t const *src, size_t srclength, char *target, size_t targsize) +mcommon_b64_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) { size_t datalength = 0; uint8_t input[3]; uint8_t output[4]; - size_t i; if (!target) { return -1; } - while (2 < srclength) { + // While we have at least three chars to read: + while (srclength > 2) { input[0] = *src++; input[1] = *src++; input[2] = *src++; srclength -= 3; output[0] = input[0] >> 2; - output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); - output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[1] = (uint8_t)(((input[0] & 0x03) << 4) + (input[1] >> 4)); + output[2] = (uint8_t)(((input[1] & 0x0f) << 2) + (input[2] >> 6)); output[3] = input[2] & 0x3f; - Assert (output[0] < 64); - Assert (output[1] < 64); - Assert (output[2] < 64); - Assert (output[3] < 64); + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); + Assert(output[3] < 64); if (datalength + 4 > targsize) { return -1; @@ -153,15 +158,13 @@ mcommon_b64_ntop (uint8_t const *src, size_t srclength, char *target, size_t tar /* Get what's left. */ input[0] = input[1] = input[2] = '\0'; - for (i = 0; i < srclength; i++) { - input[i] = *src++; - } + memcpy(input, src, srclength); output[0] = input[0] >> 2; - output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); - output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); - Assert (output[0] < 64); - Assert (output[1] < 64); - Assert (output[2] < 64); + output[1] = (uint8_t)(((input[0] & 0x03) << 4) + (input[1] >> 4)); + output[2] = (uint8_t)(((input[1] & 0x0f) << 2) + (input[2] >> 6)); + Assert(output[0] < 64); + Assert(output[1] < 64); + Assert(output[2] < 64); if (datalength + 4 > targsize) { return -1; @@ -181,7 +184,7 @@ mcommon_b64_ntop (uint8_t const *src, size_t srclength, char *target, size_t tar return -1; } target[datalength] = '\0'; /* Returned value doesn't count \0. */ - return (int) datalength; + return (int)datalength; } /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) @@ -260,51 +263,33 @@ static const uint8_t mongoc_b64rmap_space = 0xfe; static const uint8_t mongoc_b64rmap_invalid = 0xff; /* initializing the reverse map isn't thread safe, do it in pthread_once */ -#if defined(BSON_OS_UNIX) -#include -#define mongoc_common_once_t pthread_once_t -#define mongoc_common_once pthread_once -#define MONGOC_COMMON_ONCE_FUN(n) void n (void) -#define MONGOC_COMMON_ONCE_RETURN return -#define MONGOC_COMMON_ONCE_INIT PTHREAD_ONCE_INIT -#else -#define mongoc_common_once_t INIT_ONCE -#define MONGOC_COMMON_ONCE_INIT INIT_ONCE_STATIC_INIT -#define mongoc_common_once(o, c) InitOnceExecuteOnce (o, c, NULL, NULL) -#define MONGOC_COMMON_ONCE_FUN(n) BOOL CALLBACK n (PINIT_ONCE _ignored_a, PVOID _ignored_b, PVOID *_ignored_c) -#define MONGOC_COMMON_ONCE_RETURN return true -#endif - -static MONGOC_COMMON_ONCE_FUN (bson_b64_initialize_rmap) +static BSON_ONCE_FUN(bson_b64_initialize_rmap) { - int i; - unsigned char ch; - /* Null: end of string, stop parsing */ mongoc_b64rmap[0] = mongoc_b64rmap_end; - for (i = 1; i < 256; ++i) { - ch = (unsigned char) i; + mlib_foreach_urange (i, 1, 256) { + const uint8_t ch = (uint8_t)i; /* Whitespaces */ - if (bson_isspace (ch)) - mongoc_b64rmap[i] = mongoc_b64rmap_space; + if (bson_isspace(ch)) + mongoc_b64rmap[ch] = mongoc_b64rmap_space; /* Padding: stop parsing */ else if (ch == Pad64) - mongoc_b64rmap[i] = mongoc_b64rmap_end; + mongoc_b64rmap[ch] = mongoc_b64rmap_end; /* Non-base64 char */ else - mongoc_b64rmap[i] = mongoc_b64rmap_invalid; + mongoc_b64rmap[ch] = mongoc_b64rmap_invalid; } /* Fill reverse mapping for base64 chars */ - for (i = 0; Base64[i] != '\0'; ++i) - mongoc_b64rmap[(uint8_t) Base64[i]] = i; + for (uint8_t i = 0; Base64[i] != '\0'; ++i) + mongoc_b64rmap[(uint8_t)Base64[i]] = i; - MONGOC_COMMON_ONCE_RETURN; + BSON_ONCE_RETURN; } static int -mongoc_b64_pton_do (char const *src, uint8_t *target, size_t targsize) +mongoc_b64_pton_do(char const *src, uint8_t *target, size_t targsize) { int tarindex, state; uint8_t ch, ofs; @@ -313,7 +298,7 @@ mongoc_b64_pton_do (char const *src, uint8_t *target, size_t targsize) tarindex = 0; while (1) { - ch = *src++; + ch = (uint8_t)*src++; ofs = mongoc_b64rmap[ch]; if (ofs >= mongoc_b64rmap_special) { @@ -329,13 +314,13 @@ mongoc_b64_pton_do (char const *src, uint8_t *target, size_t targsize) switch (state) { case 0: - if ((size_t) tarindex >= targsize) + if ((size_t)tarindex >= targsize) return (-1); target[tarindex] = ofs << 2; state = 1; break; case 1: - if ((size_t) tarindex + 1 >= targsize) + if ((size_t)tarindex + 1 >= targsize) return (-1); target[tarindex] |= ofs >> 4; target[tarindex + 1] = (ofs & 0x0f) << 4; @@ -343,7 +328,7 @@ mongoc_b64_pton_do (char const *src, uint8_t *target, size_t targsize) state = 2; break; case 2: - if ((size_t) tarindex + 1 >= targsize) + if ((size_t)tarindex + 1 >= targsize) return (-1); target[tarindex] |= ofs >> 2; target[tarindex + 1] = (ofs & 0x03) << 6; @@ -351,14 +336,14 @@ mongoc_b64_pton_do (char const *src, uint8_t *target, size_t targsize) state = 3; break; case 3: - if ((size_t) tarindex >= targsize) + if ((size_t)tarindex >= targsize) return (-1); target[tarindex] |= ofs; tarindex++; state = 0; break; default: - abort (); + abort(); } } @@ -367,8 +352,8 @@ mongoc_b64_pton_do (char const *src, uint8_t *target, size_t targsize) * on a byte boundary, and/or with erroneous trailing characters. */ - if (ch == Pad64) { /* We got a pad char. */ - ch = *src++; /* Skip it, get next. */ + if (ch == Pad64) { /* We got a pad char. */ + ch = (uint8_t)*src++; /* Skip it, get next. */ switch (state) { case 0: /* Invalid = in first position */ case 1: /* Invalid = in second position */ @@ -376,13 +361,13 @@ mongoc_b64_pton_do (char const *src, uint8_t *target, size_t targsize) case 2: /* Valid, means one byte of info */ /* Skip any number of spaces. */ - for ((void) NULL; ch != '\0'; ch = *src++) + for ((void)NULL; ch != '\0'; ch = (uint8_t)*src++) if (mongoc_b64rmap[ch] != mongoc_b64rmap_space) break; /* Make sure there is another trailing = sign. */ if (ch != Pad64) return (-1); - ch = *src++; /* Skip the = */ + ch = (uint8_t)*src++; /* Skip the = */ /* Fall through to "single trailing =" case. */ /* FALLTHROUGH */ @@ -391,7 +376,7 @@ mongoc_b64_pton_do (char const *src, uint8_t *target, size_t targsize) * We know this char is an =. Is there anything but * whitespace after it? */ - for ((void) NULL; ch != '\0'; ch = *src++) + for ((void)NULL; ch != '\0'; ch = (uint8_t)*src++) if (mongoc_b64rmap[ch] != mongoc_b64rmap_space) return (-1); @@ -420,16 +405,15 @@ mongoc_b64_pton_do (char const *src, uint8_t *target, size_t targsize) static int -mongoc_b64_pton_len (char const *src) +mongoc_b64_pton_len(char const *src) { - int tarindex, state; - uint8_t ch, ofs; - - state = 0; - tarindex = 0; + uint8_t ch = 0; + uint8_t ofs = 0; + int state = 0; + int tarindex = 0; while (1) { - ch = *src++; + ch = (uint8_t)*src++; ofs = mongoc_b64rmap[ch]; if (ofs >= mongoc_b64rmap_special) { @@ -460,7 +444,7 @@ mongoc_b64_pton_len (char const *src) state = 0; break; default: - abort (); + abort(); } } @@ -469,8 +453,8 @@ mongoc_b64_pton_len (char const *src) * on a byte boundary, and/or with erroneous trailing characters. */ - if (ch == Pad64) { /* We got a pad char. */ - ch = *src++; /* Skip it, get next. */ + if (ch == Pad64) { /* We got a pad char. */ + ch = (uint8_t)*src++; /* Skip it, get next. */ switch (state) { case 0: /* Invalid = in first position */ case 1: /* Invalid = in second position */ @@ -478,13 +462,13 @@ mongoc_b64_pton_len (char const *src) case 2: /* Valid, means one byte of info */ /* Skip any number of spaces. */ - for ((void) NULL; ch != '\0'; ch = *src++) + for ((void)NULL; ch != '\0'; ch = (uint8_t)*src++) if (mongoc_b64rmap[ch] != mongoc_b64rmap_space) break; /* Make sure there is another trailing = sign. */ if (ch != Pad64) return (-1); - ch = *src++; /* Skip the = */ + ch = (uint8_t)*src++; /* Skip the = */ /* Fall through to "single trailing =" case. */ /* FALLTHROUGH */ @@ -493,7 +477,7 @@ mongoc_b64_pton_len (char const *src) * We know this char is an =. Is there anything but * whitespace after it? */ - for ((void) NULL; ch != '\0'; ch = *src++) + for (; ch != '\0'; ch = (uint8_t)*src++) if (mongoc_b64rmap[ch] != mongoc_b64rmap_space) return (-1); @@ -514,24 +498,24 @@ mongoc_b64_pton_len (char const *src) int -mcommon_b64_pton (char const *src, uint8_t *target, size_t targsize) +mcommon_b64_pton(char const *src, uint8_t *target, size_t targsize) { - static mongoc_common_once_t once = MONGOC_COMMON_ONCE_INIT; + static bson_once_t once = BSON_ONCE_INIT; - mongoc_common_once (&once, bson_b64_initialize_rmap); + bson_once(&once, bson_b64_initialize_rmap); if (!src) { return -1; } if (target) - return mongoc_b64_pton_do (src, target, targsize); + return mongoc_b64_pton_do(src, target, targsize); else - return mongoc_b64_pton_len (src); + return mongoc_b64_pton_len(src); } size_t -mcommon_b64_ntop_calculate_target_size (size_t raw_size) +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 @@ -544,7 +528,7 @@ mcommon_b64_ntop_calculate_target_size (size_t raw_size) } size_t -mcommon_b64_pton_calculate_target_size (size_t base64_encoded_size) +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/bsonjs/common/common-bits-private.h b/bsonjs/common/common-bits-private.h new file mode 100644 index 0000000..3a2a00e --- /dev/null +++ b/bsonjs/common/common-bits-private.h @@ -0,0 +1,50 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef MONGO_C_DRIVER_COMMON_BITS_PRIVATE_H +#define MONGO_C_DRIVER_COMMON_BITS_PRIVATE_H + +#include + + +// Round up to the next power of two uint32_t value. Saturates on overflow. +static BSON_INLINE uint32_t +mcommon_next_power_of_two_u32(uint32_t v) +{ + if (v == 0) { + return 1; + } + + // https://graphics.stanford.edu/%7Eseander/bithacks.html#RoundUpPowerOf2 + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + + if (v == 0) { + return UINT32_MAX; + } else { + return v; + } +} + + +#endif /* MONGO_C_DRIVER_COMMON_BITS_PRIVATE_H */ diff --git a/bsonjs/common/bson-dsl.h b/bsonjs/common/common-bson-dsl-private.h similarity index 57% rename from bsonjs/common/bson-dsl.h rename to bsonjs/common/common-bson-dsl-private.h index 58a14e2..748ba54 100644 --- a/bsonjs/common/bson-dsl.h +++ b/bsonjs/common/common-bson-dsl-private.h @@ -1,10 +1,10 @@ -#include "common-prelude.h" +#include -#ifndef BSON_BSON_DSL_H_INCLUDED -#define BSON_BSON_DSL_H_INCLUDED +#ifndef MONGO_C_DRIVER_COMMON_BSON_DSL_PRIVATE_H +#define MONGO_C_DRIVER_COMMON_BSON_DSL_PRIVATE_H /** - * @file bson-dsl.h + * @file common-bson-dsl-private.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 @@ -13,7 +13,9 @@ * For more information about using this DSL, refer to `bson-dsl.md`. */ -#include "bson/bson.h" +#include + +#include enum { /// Toggle this value to enable/disable debug output for all bsonDSL @@ -23,56 +25,61 @@ enum { BSON_DSL_DEBUG = 0 }; -#define _bson_thread_local BSON_IF_GNU_LIKE (__thread) BSON_IF_MSVC (__declspec (thread)) +#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))) +#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\"")) +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_disableWarnings() \ + if (1) { \ + mlib_diagnostic_push(); \ + mlib_gnu_warning_disable("-Wshadow"); \ + mlib_msvc_warning(disable : 4456); \ + } else \ + ((void)0) -#define _bsonDSL_restoreWarnings() \ - if (1) { \ - BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic pop");) \ - } else \ - ((void) 0) +#define _bsonDSL_restoreWarnings() \ + if (1) { \ + mlib_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 (); \ +#define bsonParse(Document, ...) \ + _bsonDSL_begin("bsonParse(%s)", _bsonDSL_str(Document)); \ + _bsonDSL_disableWarnings(); \ + bsonParseError = NULL; \ + bool _bvHalt = false; \ + const bool _bvContinue = false; \ + const bool _bvBreak = false; \ + (void)_bvHalt; \ + (void)_bvContinue; \ + (void)_bvBreak; \ + _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 (); \ +#define bsonVisitEach(Document, ...) \ + _bsonDSL_begin("bsonVisitEach(%s)", _bsonDSL_str(Document)); \ + _bsonDSL_disableWarnings(); \ + bool _bvHalt = false; \ + (void)_bvHalt; \ + _bsonDSL_eval(_bsonVisitEach((Document), __VA_ARGS__)); \ + _bsonDSL_restoreWarnings(); \ _bsonDSL_end #define bsonBuildContext (*_bsonBuildContextThreadLocalPtr) @@ -81,16 +88,16 @@ BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic ignored \"-Wshadow\"")) /// 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__); \ +#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) + else((void)0) /** * @brief Expands to a call to bson_append_{Kind}, with the three first @@ -103,270 +110,282 @@ BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic ignored \"-Wshadow\"")) * 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 _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"; \ - } \ +#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 (mlib_in_range(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) +#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); \ - } \ +#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__); \ +#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_unsafe_begin(_bsonBuildAppendArgs, &_bbArray)) { \ + bsonBuildError = "Error while initializing child array: " _bsonDSL_str(__VA_ARGS__); \ } else { \ - _bsonBuildAppend (_bbChildDoc, __VA_ARGS__); \ + _bsonBuildArray(_bbArray, __VA_ARGS__); \ if (!bsonBuildError) { \ - if (!bson_append_document_end (bsonBuildContext.doc, &_bbChildDoc)) { \ - bsonBuildError = "Error while finalizing document: " _bsonDSL_str (__VA_ARGS__); \ + 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 -/// 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)) +#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)) +#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)) +#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)) +#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)) +#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)) +#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))) { \ + if (!bson_append_array(_bsonBuildAppendArgs, &(Arr))) { \ bsonBuildError = "Error while appending subdocument array: " \ - "bsonArray(" _bsonDSL_str (Arr) ")"; \ + "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)) + ((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_boolean(X) _bsonArrayAppendValue(boolean(X)) +#define _bsonValueOperation_boolean(b) _bsonValueOperation_bool(b) + +#define _bsonValueOperation_oid(o) \ + if (!bson_append_oid(_bsonBuildAppendArgs, (o))) { \ + bsonBuildError = "Error while appending oid(" _bsonDSL_str(o) ")"; \ + } else \ + ((void)0) +#define _bsonArrayOperation_oid(X) _bsonArrayAppendValue(oid(X)) #define _bsonValueOperation_null \ - if (!bson_append_null (_bsonBuildAppendArgs)) { \ + if (!bson_append_null(_bsonBuildAppendArgs)) { \ bsonBuildError = "Error while appending a null"; \ } else \ - ((void) 0) -#define _bsonArrayOperation_null _bsonValueOperation (null) + ((void)0) +#define _bsonArrayOperation_null _bsonValueOperation(null) -#define _bsonArrayOperation_value(X) _bsonArrayAppendValue (value (X)) +#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) ")"; \ - } \ +#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 +#define _bsonValueOperation_binary(SubType, Data, Len) \ + if (!bson_append_binary(_bsonBuildAppendArgs, (SubType), (Data), (Len))) { \ + bsonBuildError = "Error while appending binary(" _bsonDSL_str(Data) ")"; \ + } else \ + ((void)0) + /// 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))))); \ +#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); \ - } \ +#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)); \ +#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))))); \ +#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; \ +#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 _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 _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__); \ +#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 _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 _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__); \ +#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 _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); \ +#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() @@ -375,9 +394,9 @@ BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic ignored \"-Wshadow\"")) _bsonArrayOperation_##Element; \ } -#define _bsonBuildAppendWithCurrentContext(...) _bsonDSL_mapMacro (_bsonDocOperation, ~, __VA_ARGS__) +#define _bsonBuildAppendWithCurrentContext(...) _bsonDSL_mapMacro(_bsonDocOperation, ~, __VA_ARGS__) -#define _bsonBuildArrayWithCurrentContext(...) _bsonDSL_mapMacro (_bsonArrayOperation, ~, __VA_ARGS__) +#define _bsonBuildArrayWithCurrentContext(...) _bsonDSL_mapMacro(_bsonArrayOperation, ~, __VA_ARGS__) #define _bsonDSL_Type_double BSON_TYPE_DOUBLE #define _bsonDSL_Type_utf8 BSON_TYPE_UTF8 @@ -386,9 +405,8 @@ BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic ignored \"-Wshadow\"")) #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 +// Use `boolean`, not `bool`. `bool` may be defined as a macro to `_Bool` or `int`: +#define _bsonDSL_Type_boolean 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 @@ -404,209 +422,211 @@ BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic ignored \"-Wshadow\"")) #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__); \ +#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:"); \ + if (bsonPredicate(Predicate)) { \ + _bsonDSLDebug("then:"); \ _bsonVisit_ifThen_##Then; \ } else { \ - _bsonDSLDebug ("else:"); \ + _bsonDSLDebug("else:"); \ _bsonVisit_ifElse_##Else; \ } #define _bsonVisit_ifThen(Predicate, Then) \ - if (bsonPredicate (Predicate)) { \ - _bsonDSLDebug ("then:"); \ + if (bsonPredicate(Predicate)) { \ + _bsonDSLDebug("then:"); \ _bsonVisit_ifThen_##Then; \ } else { \ - _bsonDSLDebug ("[else nothing]"); \ + _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); \ +#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); \ +#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); \ +#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)); \ - } \ +#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); \ +#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); \ - } \ +#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); \ +#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); \ +#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) ")"; \ - } \ +#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__); \ - } \ +#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__); \ +#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); + ((void)0); -#define _bsonVisitOperation_case(...) \ - _bsonDSL_begin ("case:%s", ""); \ - BSON_MAYBE_UNUSED bool _bvCaseMatched = false; \ - _bsonDSL_mapMacro (_bsonVisitCase, ~, __VA_ARGS__); \ +#define _bsonVisitOperation_case(...) \ + _bsonDSL_begin("case:%s", ""); \ + bool _bvCaseMatched = false; \ + (void)_bvCaseMatched; \ + _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; \ - } \ +#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); \ +#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]"; \ + } \ + bool _bvBreak = false; \ + bool _bvContinue = false; \ + (void)_bvBreak; \ + (void)_bvContinue; \ + 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_ASSERT (bson_init_static (&inner, data, len)); \ - _bsonVisitEach (inner, __VA_ARGS__); \ - } while (0); \ +#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_ASSERT(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_ASSERT (bson_init_static (&inner, data, len)); \ - _bsonParse (inner, __VA_ARGS__); \ +#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_ASSERT(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); \ - } \ +#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)); \ +#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) \ @@ -616,22 +636,26 @@ BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic ignored \"-Wshadow\"")) } \ } 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); \ - } \ +#define _bsonParse(Doc, ...) \ + do { \ + /* Keep track of which elements have been visited based on their index*/ \ + uint64_t _bpVisitBits_static[4] = {0}; \ + const bson_t *_bpDoc = &(Doc); \ + uint64_t *_bpVisitBits = _bpVisitBits_static; \ + size_t _bpNumVisitBitInts = sizeof _bpVisitBits_static / sizeof(uint64_t); \ + bool _bpFoundElement = false; \ + (void)_bpDoc; \ + (void)_bpVisitBits; \ + (void)_bpNumVisitBitInts; \ + (void)_bpFoundElement; \ + _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__) +#define _bsonParse_applyOps(...) _bsonDSL_mapMacro(_bsonParse_applyOp, ~, __VA_ARGS__) /// Parse one entry referrenced by the context iterator #define _bsonParse_applyOp(P, _nil, Counter) \ @@ -641,139 +665,135 @@ BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic ignored \"-Wshadow\"")) } \ } 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 _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_ARRAY_ALLOC0(new_num_visit_bit_ints, uint64_t); \ + 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]"); \ - } \ + (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); \ - } \ +#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__))); \ +#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 _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_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_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_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_type(Type) (bson_iter_type(&bsonVisitIter) == _bsonDSL_Type_##Type) #define _bsonPredicate_Condition_keyWithType(Key, Type) \ - (_bsonPredicate_Condition_allOf _bsonDSL_nothing () (key (Key), type (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))) + (_bsonPredicate_Condition_allOf _bsonDSL_nothing()(iKey(Key), type(Type))) -#define _bsonPredicate_Condition_lastElement (_bson_dsl_iter_is_last_element (&bsonVisitIter)) +#define _bsonPredicate_Condition_lastElement (_bson_dsl_iter_is_last_element(&bsonVisitIter)) -#define _bsonPredicate_Condition_isNumeric BSON_ITER_HOLDS_NUMBER (&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); \ +#define _bsonPredicate_Condition_always true +#define _bsonPredicate_Condition_never 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__)) +#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__); \ +#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 @@ -781,53 +801,53 @@ BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic ignored \"-Wshadow\"")) #define _bsonParse_ifThenElse(Condition, Then, Else) \ if ((Condition)) { \ - _bsonDSLDebug ("then:"); \ + _bsonDSLDebug("then:"); \ _bsonParse_ifThen_##Then; \ } else { \ - _bsonDSLDebug ("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 _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; \ - } \ +#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__); \ +#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__)); \ +#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 (); \ + _bsonDSL_disableWarnings(); \ struct _bsonBuildContext_t _bbCtx = { \ .doc = &(BSON), \ .parent = _bsonBuildContextThreadLocalPtr, \ .index = 0, \ }; \ _bsonBuildContextThreadLocalPtr = &_bbCtx; \ - _bsonBuildArrayWithCurrentContext (__VA_ARGS__); \ + _bsonBuildArrayWithCurrentContext(__VA_ARGS__); \ _bsonBuildContextThreadLocalPtr = _bbCtx.parent; \ - _bsonDSL_restoreWarnings (); \ + _bsonDSL_restoreWarnings(); \ } while (0) /** @@ -836,33 +856,33 @@ BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic ignored \"-Wshadow\"")) * @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 (); \ +#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__); \ +#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 /** @@ -870,7 +890,7 @@ BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic ignored \"-Wshadow\"")) */ #define bsonBuildDecl(Variable, ...) \ bson_t Variable = BSON_INITIALIZER; \ - bsonBuild (Variable, __VA_ARGS__) + bsonBuild(Variable, __VA_ARGS__) struct _bsonBuildContext_t { @@ -922,183 +942,177 @@ _bson_thread_local _bson_comdat const char *bsonBuildError = NULL; */ _bson_thread_local _bson_comdat const char *bsonParseError = NULL; -#define _bsonDSLDebug(...) _bson_dsl_debug (BSON_DSL_DEBUG, __FILE__, __LINE__, BSON_FUNC, __VA_ARGS__) +#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_dsl_test_strequal(const char *string, bool case_sensitive) { bson_iter_t it = bsonVisitIter; - if (bson_iter_type (&it) == BSON_TYPE_UTF8) { + 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)) { + 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; + return memcmp(string, s, len) == 0; } else { - return bson_strcasecmp (string, s) == 0; + 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, ...) +_bson_dsl_key_is_anyof(const char *key, const size_t keylen, int case_sensitive, ...) { va_list va; - va_start (va, case_sensitive); + va_start(va, case_sensitive); const char *str; - while ((str = va_arg (va, const char *))) { - size_t str_len = strlen (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); + if (memcmp(str, key, str_len) == 0) { + va_end(va); return true; } } else { - if (bson_strcasecmp (str, key) == 0) { - va_end (va); + if (bson_strcasecmp(str, key) == 0) { + va_end(va); return true; } } } - va_end (va); + va_end(va); return false; } static BSON_INLINE void -_bson_dsl_iter_as_doc (bson_t *into, const bson_iter_t *it) +_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 (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_ASSERT (bson_init_static (into, dataptr, len)); + BSON_ASSERT(bson_init_static(into, dataptr, len)); } } static BSON_INLINE bool -_bson_dsl_is_empty_bson (const bson_iter_t *it) +_bson_dsl_is_empty_bson(const bson_iter_t *it) { bson_t d = BSON_INITIALIZER; - _bson_dsl_iter_as_doc (&d, it); + _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_dsl_iter_is_last_element(const bson_iter_t *it) { bson_iter_t dup = *it; - return !bson_iter_next (&dup) && dup.err_off == 0; + 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, ...) +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); + fprintf(stderr, "%s:%d: [%s] bson_dsl: ", file, line, func); for (int i = 0; i < _bson_dsl_indent; ++i) { - fputs (" ", stderr); + fputs(" ", stderr); } va_list va; - va_start (va, string); - vfprintf (stderr, string, va); - va_end (va); - fputc ('\n', stderr); - fflush (stderr); + 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, ...) +static BSON_INLINE char *BSON_GNUC_PRINTF(2, 3) _bson_dsl_errorf(char **const into, const char *const fmt, ...) { if (*into) { - bson_free (*into); + bson_free(*into); *into = NULL; } va_list args; - va_start (args, fmt); - *into = bson_strdupv_printf (fmt, args); - va_end (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) +_bson_dsl_dupPath(char **into) { if (*into) { - bson_free (*into); + bson_free(*into); *into = NULL; } - char *acc = bson_strdup (""); + 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)) { + 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); + 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); + acc = bson_strdup_printf(".%s%s", bson_iter_key(&ctx->iter), prev); } - bson_free (prev); + bson_free(prev); } - *into = bson_strdup_printf ("$%s", acc); - bson_free (acc); + *into = bson_strdup_printf("$%s", acc); + bson_free(acc); } static BSON_INLINE const char * -_bsonVisitIterAs_cstr (void) +_bsonVisitIterAs_cstr(void) { - return bson_iter_utf8 (&bsonVisitIter, NULL); + 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) +_bsonVisitIterAs_int32(void) { - return bson_iter_as_bool (&bsonVisitIter); + return bson_iter_int32(&bsonVisitIter); } static BSON_INLINE bool -_bsonVisitIterAs__Bool (void) +_bsonVisitIterAs_boolean(void) { - return _bsonVisitIterAs_bool (); + return bson_iter_as_bool(&bsonVisitIter); } -#define bsonAs(Type) _bsonDSL_paste (_bsonVisitIterAs_, Type) () +#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__) +#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__)) + (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(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)) +#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)) +#define _bsonDSL_paste4(a, b, c, d) _bsonDSL_paste(a, _bsonDSL_paste3(b, c, d)) // clang-format off @@ -1281,4 +1295,4 @@ _bsonVisitIterAs__Bool (void) // clang-format on -#endif // BSON_BSON_DSL_H_INCLUDED +#endif // MONGO_C_DRIVER_COMMON_BSON_DSL_PRIVATE_H diff --git a/bsonjs/common/common-json-private.h b/bsonjs/common/common-json-private.h new file mode 100644 index 0000000..132baac --- /dev/null +++ b/bsonjs/common/common-json-private.h @@ -0,0 +1,397 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef MONGO_C_DRIVER_COMMON_JSON_PRIVATE_H +#define MONGO_C_DRIVER_COMMON_JSON_PRIVATE_H + +#include + +#define mcommon_iso8601_string_append COMMON_NAME(iso8601_string_append) +#define mcommon_json_append_escaped COMMON_NAME(json_append_escaped) +#define mcommon_json_append_value_double COMMON_NAME(json_append_value_double) +#define mcommon_json_append_value_decimal128 COMMON_NAME(json_append_value_decimal128) +#define mcommon_json_append_value_oid COMMON_NAME(json_append_value_oid) +#define mcommon_json_append_value_binary COMMON_NAME(json_append_value_binary) +#define mcommon_json_append_value_date_time COMMON_NAME(json_append_value_date_time) +#define mcommon_json_append_value_timestamp COMMON_NAME(json_append_value_timestamp) +#define mcommon_json_append_value_regex COMMON_NAME(json_append_value_regex) +#define mcommon_json_append_value_dbpointer COMMON_NAME(json_append_value_dbpointer) +#define mcommon_json_append_value_code COMMON_NAME(json_append_value_code) +#define mcommon_json_append_value_codewscope COMMON_NAME(json_append_value_codewscope) +#define mcommon_json_append_value_symbol COMMON_NAME(json_append_value_symbol) +#define mcommon_json_append_bson_values COMMON_NAME(json_append_bson_values) +#define mcommon_json_append_bson_document COMMON_NAME(json_append_bson_document) +#define mcommon_json_append_bson_array COMMON_NAME(json_append_bson_array) + +// Needed by libbson and common-json +#ifndef BSON_MAX_RECURSION +#define BSON_MAX_RECURSION 200 +#endif + +// Needed by libbson and common-json +#define BSON_REGEX_OPTIONS_SORTED "ilmsux" + +/** + * @brief Append an ISO 8601 formatted date, given 64-bit milliseconds since the epoch + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param msec_since_epoch Milliseconds since Jan 1 1970 UTC + * @returns true on success, false if this 'append' has exceeded its max length + */ +bool +mcommon_iso8601_string_append(mcommon_string_append_t *append, int64_t msec_since_epoch); + +/** + * @brief Append a UTF-8 string with all special characters escaped + * + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param str UTF-8 string to escape and append + * @param len Length of 'str' in bytes + * @param allow_nul true if internal "00" bytes or "C0 80" sequences should be encoded as "\u0000", false to treat + * them as invalid data + * @returns true on success, false if this 'append' has exceeded its max length or if we encountered invalid UTF-8 or + * disallowed NUL bytes in 'str' + * + * The string may include internal NUL characters. It does not need to be NUL terminated. + * The two-byte sequence "C0 80" is also interpreted as an internal NUL, for historical reasons. This sequence is + * considered invalid according to RFC3629. + */ +bool +mcommon_json_append_escaped(mcommon_string_append_t *append, const char *str, uint32_t len, bool allow_nul); + +/** + * @brief Append a comma separator string to appear between values + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @returns true on success, false if this 'append' has exceeded its max length + */ +static BSON_INLINE bool +mcommon_json_append_separator(mcommon_string_append_t *append) +{ + return mcommon_string_append(append, ", "); +} + +/** + * @brief Append a quoted and escaped key and key-value separator + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param str UTF-8 string to escape and append + * @param len Length of 'str' in bytes + * @returns true on success, false if this 'append' has exceeded its max length or if we encountered invalid UTF-8 or + * disallowed NUL bytes in 'str' + * + * See mcommon_json_append_escaped. NUL values in keys are never allowed. + */ +static BSON_INLINE bool +mcommon_json_append_key(mcommon_string_append_t *append, const char *str, uint32_t len) +{ + return mcommon_string_append(append, "\"") && mcommon_json_append_escaped(append, str, len, false) && + mcommon_string_append(append, "\" : "); +} + +/** + * @brief Append a quoted and escaped string + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param str UTF-8 string to escape and append + * @param len Length of 'str' in bytes + * @param allow_nul true if internal "00" bytes or "C0 80" sequences should be encoded as "\u0000", false to treat them + * as invalid data + * @returns true on success, false if this 'append' has exceeded its max length or if we encountered invalid UTF-8 or + * disallowed NUL bytes in 'str' + * + * See mcommon_json_append_escaped. + */ +static BSON_INLINE bool +mcommon_json_append_value_utf8(mcommon_string_append_t *append, const char *str, uint32_t len, bool allow_nul) +{ + return mcommon_string_append(append, "\"") && mcommon_json_append_escaped(append, str, len, allow_nul) && + mcommon_string_append(append, "\""); +} + +/** + * @brief Append an int32_t value, serialized according to a bson_json_mode_t + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param value Integer value + * @param mode One of the JSON serialization modes, as a bson_json_mode_t. + * @returns true on success, false if this 'append' has exceeded its max length + */ +static BSON_INLINE bool +mcommon_json_append_value_int32(mcommon_string_append_t *append, int32_t value, bson_json_mode_t mode) +{ + return mode == BSON_JSON_MODE_CANONICAL + ? mcommon_string_append_printf(append, "{ \"$numberInt\" : \"%" PRId32 "\" }", value) + : mcommon_string_append_printf(append, "%" PRId32, value); +} + +/** + * @brief Append an int64_t value, serialized according to a bson_json_mode_t + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param value Integer value + * @param mode One of the JSON serialization modes, as a bson_json_mode_t. + * @returns true on success, false if this 'append' has exceeded its max length + */ +static BSON_INLINE bool +mcommon_json_append_value_int64(mcommon_string_append_t *append, int64_t value, bson_json_mode_t mode) +{ + return mode == BSON_JSON_MODE_CANONICAL + ? mcommon_string_append_printf(append, "{ \"$numberLong\" : \"%" PRId64 "\" }", value) + : mcommon_string_append_printf(append, "%" PRId64, value); +} + +/** + * @brief Append a JSON compatible bool value + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param bool Boolean value + * @returns true on success, false if this 'append' has exceeded its max length + */ +static BSON_INLINE bool +mcommon_json_append_value_bool(mcommon_string_append_t *append, bool value) +{ + return mcommon_string_append(append, value ? "true" : "false"); +} + +/** + * @brief Append an $undefined value + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @returns true on success, false if this 'append' has exceeded its max length + */ +static BSON_INLINE bool +mcommon_json_append_value_undefined(mcommon_string_append_t *append) +{ + return mcommon_string_append(append, "{ \"$undefined\" : true }"); +} + +/** + * @brief Append a null value + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @returns true on success, false if this 'append' has exceeded its max length + */ +static BSON_INLINE bool +mcommon_json_append_value_null(mcommon_string_append_t *append) +{ + return mcommon_string_append(append, "null"); +} + +/** + * @brief Append a $minKey value + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @returns true on success, false if this 'append' has exceeded its max length + */ +static BSON_INLINE bool +mcommon_json_append_value_minkey(mcommon_string_append_t *append) +{ + return mcommon_string_append(append, "{ \"$minKey\" : 1 }"); +} + +/** + * @brief Append a $maxKey value + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @returns true on success, false if this 'append' has exceeded its max length + */ +static BSON_INLINE bool +mcommon_json_append_value_maxkey(mcommon_string_append_t *append) +{ + return mcommon_string_append(append, "{ \"$maxKey\" : 1 }"); +} + +/** + * @brief Append a double-precision floating point value + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param value Double-precision floating point value + * @param mode One of the JSON serialization modes, as a bson_json_mode_t. + * @returns true on success, false if this 'append' has exceeded its max length + */ +bool +mcommon_json_append_value_double(mcommon_string_append_t *append, double value, bson_json_mode_t mode); + +/** + * @brief Append a decimal128 value + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param value decimal128 value to copy + * @returns true on success, false if this 'append' has exceeded its max length + */ +bool +mcommon_json_append_value_decimal128(mcommon_string_append_t *append, const bson_decimal128_t *value); + +/** + * @brief Append the $oid JSON serialization of an ObjectId value + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param value bson_oid_t value to copy + * @returns true on success, false if this 'append' has exceeded its max length + */ +bool +mcommon_json_append_value_oid(mcommon_string_append_t *append, const bson_oid_t *value); + +/** + * @brief Append the JSON serialization of a BSON binary value + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param subtype Subtype code, identifying the format within the base64-encoded binary block + * @param bytes Bytes to be base64 encoded + * @param byte_count Number of bytes + * @param mode One of the JSON serialization modes, as a bson_json_mode_t + * @returns true on success, false if this 'append' has exceeded its max length + */ +bool +mcommon_json_append_value_binary(mcommon_string_append_t *append, + bson_subtype_t subtype, + const uint8_t *bytes, + uint32_t byte_count, + bson_json_mode_t mode); + +/** + * @brief Append the JSON serialization of a BSON date and time + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param msec_since_epoch Milliseconds since Jan 1 1970 + * @param mode One of the JSON serialization modes, as a bson_json_mode_t + * @returns true on success, false if this 'append' has exceeded its max length + */ +bool +mcommon_json_append_value_date_time(mcommon_string_append_t *append, int64_t msec_since_epoch, bson_json_mode_t mode); + +/** + * @brief Append the JSON serialization of a BSON timestamp value + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param timestamp 32-bit timestamp value + * @param increment 32-bit increment value + * @returns true on success, false if this 'append' has exceeded its max length + */ +bool +mcommon_json_append_value_timestamp(mcommon_string_append_t *append, uint32_t timestamp, uint32_t increment); + +/** + * @brief Append the JSON serialization of a BSON regular expression + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param pattern Regular expression pattern, as a UTF-8 string + * @param pattern_len Length of pattern string, in bytes + * @param options Regular expression options, as a UTF-8 string + * @param options_len Length of the options string, in bytes + * @param mode One of the JSON serialization modes, as a bson_json_mode_t + * @returns true on success, false if this 'append' has exceeded its max length + */ +bool +mcommon_json_append_value_regex(mcommon_string_append_t *append, + const char *pattern, + uint32_t pattern_len, + const char *options, + size_t options_len, + bson_json_mode_t mode); + +/** + * @brief Append the JSON serialization of a BSON legacy DBPointer + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param collection Collection name, as a UTF-8 string + * @param collection_len Length of collection name string, in bytes + * @param oid Optional ObjectId reference, or NULL + * @param mode One of the JSON serialization modes, as a bson_json_mode_t + * @returns true on success, false if this 'append' has exceeded its max length + */ +bool +mcommon_json_append_value_dbpointer(mcommon_string_append_t *append, + const char *collection, + uint32_t collection_len, + const bson_oid_t *oid, + bson_json_mode_t mode); + +/** + * @brief Append the JSON serialization of a BSON legacy code object + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param code Code string, in UTF-8 + * @param code_len Length of code string, in bytes + * @returns true on success, false if this 'append' has exceeded its max length + */ +bool +mcommon_json_append_value_code(mcommon_string_append_t *append, const char *code, uint32_t code_len); + +/** + * @brief Append the JSON serialization of a BSON legacy code-with-scope object + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param code Code string, in UTF-8 + * @param code_len Length of code string, in bytes + * @param scope Scope as a bson_t document + * @param mode One of the JSON serialization modes, as a bson_json_mode_t + * @param max_depth Maximum allowed number of document/array nesting levels below this one + * @returns true if the input bson was valid, even if we reached max length. false on invalid BSON. + */ +bool +mcommon_json_append_value_codewscope(mcommon_string_append_t *append, + const char *code, + uint32_t code_len, + const bson_t *scope, + bson_json_mode_t mode, + unsigned max_depth); + +/** + * @brief Append the JSON serialization of a BSON legacy symbol object + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param symbol Symbol string, in UTF-8 + * @param symbol_len Length of symbol string, in bytes + * @param mode One of the JSON serialization modes, as a bson_json_mode_t. + * @returns true on success, false if this 'append' has exceeded its max length + */ +bool +mcommon_json_append_value_symbol(mcommon_string_append_t *append, + const char *symbol, + uint32_t symbol_len, + bson_json_mode_t mode); + +/** + * @brief Append all JSON-serialized values from a bson_t + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param bson bson_t document or array + * @param mode One of the JSON serialization modes, as a bson_json_mode_t + * @param has_keys true if this is a document, false if this is an array + * @param max_depth Maximum allowed number of document/array nesting levels below this one + * @returns true if the input bson was valid, even if we reached max length. false on invalid BSON. + * + * This generates keys, values, and separators but does not enclose the result in {} or []. + * Note that the return value reflects the status of BSON decoding, not string appending. + * The append status can be read using mcommon_string_status_from_append() if needed. + * If encoding was stopped early due to the max depth limit or max length, invalid input may go unnoticed. + */ +bool +mcommon_json_append_bson_values( + mcommon_string_append_t *append, const bson_t *bson, bson_json_mode_t mode, bool has_keys, unsigned max_depth); + +/** + * @brief Append a BSON document serialized as a JSON document + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param bson bson_t document + * @param mode One of the JSON serialization modes, as a bson_json_mode_t + * @param max_depth Maximum allowed number of document/array nesting levels *including* this one. If zero, appends "{ + * ... }". + * @returns true if the input bson was valid, even if we reached max length. false on invalid BSON. + */ +bool +mcommon_json_append_bson_document(mcommon_string_append_t *append, + const bson_t *bson, + bson_json_mode_t mode, + unsigned max_depth); + +/** + * @brief Append a BSON document serialized as a JSON array + * @param append A bounded string append, initialized with mcommon_string_set_append() + * @param bson bson_t to interpret as an array + * @param mode One of the JSON serialization modes, as a bson_json_mode_t + * @param max_depth Maximum allowed number of document/array nesting levels *including* this one. If zero, appends "[ + * ... ]". + * @returns true if the input bson was valid, even if we reached max length. false on invalid BSON. + */ +bool +mcommon_json_append_bson_array(mcommon_string_append_t *append, + const bson_t *bson, + bson_json_mode_t mode, + unsigned max_depth); + +#endif /* MONGO_C_DRIVER_COMMON_JSON_PRIVATE_H */ diff --git a/bsonjs/common/common-json.c b/bsonjs/common/common-json.c new file mode 100644 index 0000000..2dda278 --- /dev/null +++ b/bsonjs/common/common-json.c @@ -0,0 +1,765 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include +#include + +#include + +#include + + +typedef struct { + mcommon_string_append_t *append; + unsigned max_depth; + bson_json_mode_t mode; + bool has_keys; + bool not_first_item; + bool is_corrupt; +} mcommon_json_append_visit_t; + + +static bool +mcommon_json_append_visit_utf8( + const bson_iter_t *iter, const char *key, size_t v_utf8_len, const char *v_utf8, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + if (!mlib_in_range(uint32_t, v_utf8_len)) { + mcommon_string_append_overflow(state->append); + return true; + } + return !mcommon_json_append_value_utf8(state->append, v_utf8, (uint32_t)v_utf8_len, true); +} + +static bool +mcommon_json_append_visit_int32(const bson_iter_t *iter, const char *key, int32_t v_int32, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + return !mcommon_json_append_value_int32(state->append, v_int32, state->mode); +} + +static bool +mcommon_json_append_visit_int64(const bson_iter_t *iter, const char *key, int64_t v_int64, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + return !mcommon_json_append_value_int64(state->append, v_int64, state->mode); +} + +static bool +mcommon_json_append_visit_decimal128(const bson_iter_t *iter, + const char *key, + const bson_decimal128_t *value, + void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + return !mcommon_json_append_value_decimal128(state->append, value); +} + +static bool +mcommon_json_append_visit_double(const bson_iter_t *iter, const char *key, double v_double, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + return !mcommon_json_append_value_double(state->append, v_double, state->mode); +} + +static bool +mcommon_json_append_visit_undefined(const bson_iter_t *iter, const char *key, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + return !mcommon_json_append_value_undefined(state->append); +} + +static bool +mcommon_json_append_visit_null(const bson_iter_t *iter, const char *key, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + return !mcommon_json_append_value_null(state->append); +} + +static bool +mcommon_json_append_visit_oid(const bson_iter_t *iter, const char *key, const bson_oid_t *oid, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + return !mcommon_json_append_value_oid(state->append, oid); +} + +static bool +mcommon_json_append_visit_binary(const bson_iter_t *iter, + const char *key, + bson_subtype_t v_subtype, + size_t v_binary_len, + const uint8_t *v_binary, + void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + if (!mlib_in_range(uint32_t, v_binary_len)) { + mcommon_string_append_overflow(state->append); + return true; + } + return !mcommon_json_append_value_binary(state->append, v_subtype, v_binary, (uint32_t)v_binary_len, state->mode); +} + +static bool +mcommon_json_append_visit_bool(const bson_iter_t *iter, const char *key, bool v_bool, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + return !mcommon_json_append_value_bool(state->append, v_bool); +} + +static bool +mcommon_json_append_visit_date_time(const bson_iter_t *iter, const char *key, int64_t msec_since_epoch, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + return !mcommon_json_append_value_date_time(state->append, msec_since_epoch, state->mode); +} + +static bool +mcommon_json_append_visit_regex( + const bson_iter_t *iter, const char *key, const char *v_regex, const char *v_options, void *data) +{ + mcommon_json_append_visit_t *state = data; + size_t v_regex_len = strlen(v_regex); + size_t v_options_len = strlen(v_options); + BSON_UNUSED(iter); + BSON_UNUSED(key); + if (!mlib_in_range(uint32_t, v_regex_len)) { + mcommon_string_append_overflow(state->append); + return true; + } + return !mcommon_json_append_value_regex( + state->append, v_regex, (uint32_t)v_regex_len, v_options, v_options_len, state->mode); +} + +static bool +mcommon_json_append_visit_timestamp( + const bson_iter_t *iter, const char *key, uint32_t v_timestamp, uint32_t v_increment, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + return !mcommon_json_append_value_timestamp(state->append, v_timestamp, v_increment); +} + +static bool +mcommon_json_append_visit_dbpointer(const bson_iter_t *iter, + const char *key, + size_t v_collection_len, + const char *v_collection, + const bson_oid_t *v_oid, + void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + if (!mlib_in_range(uint32_t, v_collection_len)) { + mcommon_string_append_overflow(state->append); + return true; + } + return !mcommon_json_append_value_dbpointer( + state->append, v_collection, (uint32_t)v_collection_len, v_oid, state->mode); +} + +static bool +mcommon_json_append_visit_minkey(const bson_iter_t *iter, const char *key, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + return !mcommon_json_append_value_minkey(state->append); +} + +static bool +mcommon_json_append_visit_maxkey(const bson_iter_t *iter, const char *key, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + return !mcommon_json_append_value_maxkey(state->append); +} + +static bool +mcommon_json_append_visit_before(const bson_iter_t *iter, const char *key, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + + if (!mcommon_string_status_from_append(state->append)) { + return true; + } + + if (state->not_first_item) { + if (!mcommon_json_append_separator(state->append)) { + return true; + } + } else { + state->not_first_item = true; + } + + if (state->has_keys) { + size_t key_len = strlen(key); + if (!mlib_in_range(uint32_t, key_len)) { + mcommon_string_append_overflow(state->append); + return true; + } + if (!mcommon_json_append_key(state->append, key, (uint32_t)key_len)) { + return true; + } + } + + return false; +} + +static bool +mcommon_json_append_visit_after(const bson_iter_t *iter, const char *key, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + return !mcommon_string_status_from_append(state->append); +} + +static void +mcommon_json_append_visit_corrupt(const bson_iter_t *iter, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + state->is_corrupt = true; +} + +static bool +mcommon_json_append_visit_code( + const bson_iter_t *iter, const char *key, size_t v_code_len, const char *v_code, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + if (!mlib_in_range(uint32_t, v_code_len)) { + mcommon_string_append_overflow(state->append); + return true; + } + return !mcommon_json_append_value_code(state->append, v_code, (uint32_t)v_code_len); +} + +static bool +mcommon_json_append_visit_symbol( + const bson_iter_t *iter, const char *key, size_t v_symbol_len, const char *v_symbol, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + if (!mlib_in_range(uint32_t, v_symbol_len)) { + mcommon_string_append_overflow(state->append); + return true; + } + return !mcommon_json_append_value_symbol(state->append, v_symbol, (uint32_t)v_symbol_len, state->mode); +} + +static bool +mcommon_json_append_visit_codewscope( + const bson_iter_t *iter, const char *key, size_t v_code_len, const char *v_code, const bson_t *v_scope, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + if (!mlib_in_range(uint32_t, v_code_len)) { + mcommon_string_append_overflow(state->append); + return true; + } + if (mcommon_json_append_value_codewscope( + state->append, v_code, (uint32_t)v_code_len, v_scope, state->mode, state->max_depth)) { + return !mcommon_string_status_from_append(state->append); + } else { + state->is_corrupt = true; + return true; + } +} + +static bool +mcommon_json_append_visit_document(const bson_iter_t *iter, const char *key, const bson_t *v_document, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + if (mcommon_json_append_bson_document(state->append, v_document, state->mode, state->max_depth)) { + return !mcommon_string_status_from_append(state->append); + } else { + state->is_corrupt = true; + return true; + } +} + +static bool +mcommon_json_append_visit_array(const bson_iter_t *iter, const char *key, const bson_t *v_array, void *data) +{ + mcommon_json_append_visit_t *state = data; + BSON_UNUSED(iter); + BSON_UNUSED(key); + if (mcommon_json_append_bson_array(state->append, v_array, state->mode, state->max_depth)) { + return !mcommon_string_status_from_append(state->append); + } else { + state->is_corrupt = true; + return true; + } +} + +bool +mcommon_json_append_bson_values( + mcommon_string_append_t *append, const bson_t *bson, bson_json_mode_t mode, bool has_keys, unsigned max_depth) +{ + mcommon_json_append_visit_t state = {.append = append, .max_depth = max_depth, .mode = mode, .has_keys = has_keys}; + bson_iter_t iter; + if (!bson_iter_init(&iter, bson)) { + return false; + } + static const bson_visitor_t visitors = { + mcommon_json_append_visit_before, mcommon_json_append_visit_after, mcommon_json_append_visit_corrupt, + mcommon_json_append_visit_double, mcommon_json_append_visit_utf8, mcommon_json_append_visit_document, + mcommon_json_append_visit_array, mcommon_json_append_visit_binary, mcommon_json_append_visit_undefined, + mcommon_json_append_visit_oid, mcommon_json_append_visit_bool, mcommon_json_append_visit_date_time, + mcommon_json_append_visit_null, mcommon_json_append_visit_regex, mcommon_json_append_visit_dbpointer, + mcommon_json_append_visit_code, mcommon_json_append_visit_symbol, mcommon_json_append_visit_codewscope, + mcommon_json_append_visit_int32, mcommon_json_append_visit_timestamp, mcommon_json_append_visit_int64, + mcommon_json_append_visit_maxkey, mcommon_json_append_visit_minkey, NULL, /* visit_unsupported_type */ + mcommon_json_append_visit_decimal128, + }; + /* Note that early exit from bson_iter_visit_all does not affect our success, which is based only on BSON validity. + * BSON errors will set is_corrupt if they prevent full traversal, but non-fatal parse errors (like invalid UTF-8) + * may let bson_iter_visit_all() succeed while leaving an error status in iter.err_off. */ + (void)bson_iter_visit_all(&iter, &visitors, &state); + return iter.err_off == 0 && !state.is_corrupt; +} + +static BSON_INLINE bool +mcommon_json_append_bson_container(mcommon_string_append_t *append, + const bson_t *bson, + bson_json_mode_t mode, + unsigned max_depth, + bool has_keys, + const char *empty, + const char *begin_non_empty, + const char *end_non_empty, + const char *omitted) +{ + // Note that the return value here is bson validity, not append status. + if (bson_empty(bson)) { + (void)mcommon_string_append(append, empty); + return true; + } else if (max_depth == 0) { + (void)mcommon_string_append(append, omitted); + return true; + } else { + (void)mcommon_string_append(append, begin_non_empty); + bool result = mcommon_json_append_bson_values(append, bson, mode, has_keys, max_depth - 1u); + (void)mcommon_string_append(append, end_non_empty); + return result; + } +} + +bool +mcommon_json_append_bson_document(mcommon_string_append_t *append, + const bson_t *bson, + bson_json_mode_t mode, + unsigned max_depth) +{ + return mcommon_json_append_bson_container(append, bson, mode, max_depth, true, "{ }", "{ ", " }", "{ ... }"); +} + +bool +mcommon_json_append_bson_array(mcommon_string_append_t *append, + const bson_t *bson, + bson_json_mode_t mode, + unsigned max_depth) +{ + return mcommon_json_append_bson_container(append, bson, mode, max_depth, false, "[ ]", "[ ", " ]", "[ ... ]"); +} + +/** + * @brief Like mcommon_string_append_printf (append, "\\u%04x", c) but intended to be more optimizable. + */ +static BSON_INLINE bool +mcommon_json_append_hex_char(mcommon_string_append_t *append, uint16_t c) +{ + static const char digit_table[] = "0123456789abcdef"; + char hex_char[6]; + hex_char[0] = '\\'; + hex_char[1] = 'u'; + hex_char[2] = digit_table[0xf & (c >> 12)]; + hex_char[3] = digit_table[0xf & (c >> 8)]; + hex_char[4] = digit_table[0xf & (c >> 4)]; + hex_char[5] = digit_table[0xf & c]; + return mcommon_string_append_bytes(append, hex_char, 6); +} + +/** + * @brief Test whether a byte may require special processing in mcommon_json_append_escaped. + * @returns true for bytes in the range 0x00 - 0x1F, '\\', '\"', and 0xC0. + */ +static BSON_INLINE bool +mcommon_json_append_escaped_considers_byte_as_special(uint8_t byte) +{ + static const uint64_t table[4] = { + 0x00000004ffffffffull, // 0x00-0x1F (control), 0x22 (") + 0x0000000010000000ull, // 0x5C (') + 0x0000000000000000ull, // none + 0x0000000000000001ull, // 0xC0 (Possible two-byte NUL) + }; + return 0 != (table[byte >> 6] & (1ull << (byte & 0x3f))); +} + +/** + * @brief Measure the number of consecutive non-special bytes. + */ +static BSON_INLINE uint32_t +mcommon_json_append_escaped_count_non_special_bytes(const char *str, uint32_t len) +{ + uint32_t result = 0; + // Good candidate for architecture-specific optimizations. + // SSE4 strcspn is nearly what we want, but our table of special bytes would be too large (34 > 16) + while (len) { + if (mcommon_json_append_escaped_considers_byte_as_special((uint8_t)*str)) { + break; + } + result++; + str++; + len--; + } + return result; +} + +bool +mcommon_json_append_escaped(mcommon_string_append_t *append, const char *str, uint32_t len, bool allow_nul) +{ + BSON_ASSERT_PARAM(append); + BSON_ASSERT_PARAM(str); + + // Repeatedly handle runs of zero or more non-special bytes punctuated by a potentially-special sequence. + uint32_t non_special_len = mcommon_json_append_escaped_count_non_special_bytes(str, len); + while (len) { + if (!mcommon_string_append_bytes(append, str, non_special_len)) { + return false; + } + str += non_special_len; + len -= non_special_len; + if (len) { + char c = *str; + switch (c) { + case '"': + if (!mcommon_string_append(append, "\\\"")) { + return false; + } + break; + case '\\': + if (!mcommon_string_append(append, "\\\\")) { + return false; + } + break; + case '\b': + if (!mcommon_string_append(append, "\\b")) { + return false; + } + break; + case '\f': + if (!mcommon_string_append(append, "\\f")) { + return false; + } + break; + case '\n': + if (!mcommon_string_append(append, "\\n")) { + return false; + } + break; + case '\r': + if (!mcommon_string_append(append, "\\r")) { + return false; + } + break; + case '\t': + if (!mcommon_string_append(append, "\\t")) { + return false; + } + break; + case '\0': + if (!allow_nul || !mcommon_json_append_hex_char(append, 0)) { + return false; + } + break; + case '\xc0': // Could be a 2-byte NUL, or could begin another non-special run + if (len >= 2 && str[1] == '\x80') { + if (!allow_nul || !mcommon_json_append_hex_char(append, 0)) { + return false; + } + str++; + len--; + } else { + // Wasn't "C0 80". Begin a non-special run with the "C0" byte, which is usually special. + non_special_len = mcommon_json_append_escaped_count_non_special_bytes(str + 1, len - 1) + 1; + continue; + } + break; + default: + BSON_ASSERT(c > 0x00 && c < 0x20); + if (!mcommon_json_append_hex_char(append, c)) { + return false; + } + break; + } + str++; + len--; + non_special_len = mcommon_json_append_escaped_count_non_special_bytes(str, len); + } + } + return mcommon_string_status_from_append(append); +} + +bool +mcommon_iso8601_string_append(mcommon_string_append_t *append, int64_t msec_since_epoch) +{ + time_t t; + int64_t msec_part; + char buf[64]; + + msec_part = msec_since_epoch % 1000; + t = (time_t)(msec_since_epoch / 1000); + +#ifdef BSON_HAVE_GMTIME_R + { + struct tm posix_date; + gmtime_r(&t, &posix_date); + strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%S", &posix_date); + } +#elif defined(_MSC_VER) + { + /* Windows gmtime_s is thread-safe */ + struct tm time_buf; + gmtime_s(&time_buf, &t); + strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%S", &time_buf); + } +#else + strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%S", gmtime(&t)); +#endif + + if (msec_part) { + return mcommon_string_append_printf(append, "%s.%03" PRId64 "Z", buf, msec_part); + } else { + return mcommon_string_append_printf(append, "%sZ", buf); + } +} + +bool +mcommon_json_append_value_double(mcommon_string_append_t *append, double value, bson_json_mode_t mode) +{ + /* 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. */ + bool legacy = + mode == BSON_JSON_MODE_LEGACY || (mode == BSON_JSON_MODE_RELAXED && !(value != value || value * 0 != 0)); + + if (!legacy) { + mcommon_string_append(append, "{ \"$numberDouble\" : \""); + } + + if (!legacy && value != value) { + mcommon_string_append(append, "NaN"); + } else if (!legacy && value * 0 != 0) { + if (value > 0) { + mcommon_string_append(append, "Infinity"); + } else { + mcommon_string_append(append, "-Infinity"); + } + } else { + const mcommon_string_t *string = mcommon_string_from_append(append); + uint32_t start_len = string->len; + if (mcommon_string_append_printf(append, "%.20g", value)) { + /* ensure trailing ".0" to distinguish "3" from "3.0" */ + if (strspn(&string->str[start_len], "0123456789-") == string->len - start_len) { + mcommon_string_append(append, ".0"); + } + } + } + + if (!legacy) { + mcommon_string_append(append, "\" }"); + } + + return mcommon_string_status_from_append(append); +} + +bool +mcommon_json_append_value_decimal128(mcommon_string_append_t *append, const bson_decimal128_t *value) +{ + char decimal128_string[BSON_DECIMAL128_STRING]; + bson_decimal128_to_string(value, decimal128_string); + + return mcommon_string_append(append, "{ \"$numberDecimal\" : \"") && + mcommon_string_append(append, decimal128_string) && mcommon_string_append(append, "\" }"); +} + +bool +mcommon_json_append_value_oid(mcommon_string_append_t *append, const bson_oid_t *value) +{ + return mcommon_string_append(append, "{ \"$oid\" : \"") && mcommon_string_append_oid_as_hex(append, value) && + mcommon_string_append(append, "\" }"); +} + +bool +mcommon_json_append_value_binary(mcommon_string_append_t *append, + bson_subtype_t subtype, + const uint8_t *bytes, + uint32_t byte_count, + bson_json_mode_t mode) +{ + if (mode == BSON_JSON_MODE_CANONICAL || mode == BSON_JSON_MODE_RELAXED) { + return mcommon_string_append(append, "{ \"$binary\" : { \"base64\" : \"") && + mcommon_string_append_base64_encode(append, bytes, byte_count) && + mcommon_string_append_printf(append, "\", \"subType\" : \"%02x\" } }", (unsigned int)subtype); + } else { + return mcommon_string_append(append, "{ \"$binary\" : \"") && + mcommon_string_append_base64_encode(append, bytes, byte_count) && + mcommon_string_append_printf(append, "\", \"$type\" : \"%02x\" }", (unsigned int)subtype); + } +} + +bool +mcommon_json_append_value_date_time(mcommon_string_append_t *append, int64_t msec_since_epoch, bson_json_mode_t mode) +{ + const int64_t y10k = 253402300800000; // 10000-01-01T00:00:00Z in milliseconds since the epoch. + + if (mode == BSON_JSON_MODE_CANONICAL || + (mode == BSON_JSON_MODE_RELAXED && (msec_since_epoch < 0 || msec_since_epoch >= y10k))) { + return mcommon_string_append_printf( + append, "{ \"$date\" : { \"$numberLong\" : \"%" PRId64 "\" } }", msec_since_epoch); + } else if (mode == BSON_JSON_MODE_RELAXED) { + return mcommon_string_append(append, "{ \"$date\" : \"") && + mcommon_iso8601_string_append(append, msec_since_epoch) && mcommon_string_append(append, "\" }"); + } else { + return mcommon_string_append_printf(append, "{ \"$date\" : %" PRId64 " }", msec_since_epoch); + } +} + +bool +mcommon_json_append_value_timestamp(mcommon_string_append_t *append, uint32_t timestamp, uint32_t increment) +{ + BSON_ASSERT_PARAM(append); + return mcommon_string_append_printf(append, "{ \"$timestamp\" : { \"t\" : %u, \"i\" : %u } }", timestamp, increment); +} + +bool +mcommon_json_append_value_regex(mcommon_string_append_t *append, + const char *pattern, + uint32_t pattern_len, + const char *options, + size_t options_len, + bson_json_mode_t mode) +{ + if (mode == BSON_JSON_MODE_CANONICAL || mode == BSON_JSON_MODE_RELAXED) { + return mcommon_string_append(append, "{ \"$regularExpression\" : { \"pattern\" : \"") && + mcommon_json_append_escaped(append, pattern, pattern_len, false) && + mcommon_string_append(append, "\", \"options\" : \"") && + mcommon_string_append_selected_chars(append, BSON_REGEX_OPTIONS_SORTED, options, options_len) && + mcommon_string_append(append, "\" } }"); + } else { + return mcommon_string_append(append, "{ \"$regex\" : \"") && + mcommon_json_append_escaped(append, pattern, pattern_len, false) && + mcommon_string_append(append, "\", \"$options\" : \"") && + mcommon_string_append_selected_chars(append, BSON_REGEX_OPTIONS_SORTED, options, options_len) && + mcommon_string_append(append, "\" }"); + } +} + +bool +mcommon_json_append_value_dbpointer(mcommon_string_append_t *append, + const char *collection, + uint32_t collection_len, + const bson_oid_t *oid, + bson_json_mode_t mode) +{ + if (mode == BSON_JSON_MODE_CANONICAL || mode == BSON_JSON_MODE_RELAXED) { + return mcommon_string_append(append, "{ \"$dbPointer\" : { \"$ref\" : \"") && + mcommon_json_append_escaped(append, collection, collection_len, false) && + mcommon_string_append(append, "\"") && + (!oid || (mcommon_string_append(append, ", \"$id\" : ") && mcommon_json_append_value_oid(append, oid))) && + mcommon_string_append(append, " } }"); + } else { + return mcommon_string_append(append, "{ \"$ref\" : \"") && + mcommon_json_append_escaped(append, collection, collection_len, false) && + mcommon_string_append(append, "\"") && + (!oid || + (mcommon_string_append(append, ", \"$id\" : \"") && mcommon_string_append_oid_as_hex(append, oid))) && + mcommon_string_append(append, "\" }"); + } +} + +bool +mcommon_json_append_value_code(mcommon_string_append_t *append, const char *code, uint32_t code_len) +{ + return mcommon_string_append(append, "{ \"$code\" : \"") && + mcommon_json_append_escaped(append, code, code_len, true) && mcommon_string_append(append, "\" }"); +} + +bool +mcommon_json_append_value_codewscope(mcommon_string_append_t *append, + const char *code, + uint32_t code_len, + const bson_t *scope, + bson_json_mode_t mode, + unsigned max_depth) +{ + // Note that the return value here is bson validity, not append status. + (void)mcommon_string_append(append, "{ \"$code\" : \""); + (void)mcommon_json_append_escaped(append, code, code_len, true); + (void)mcommon_string_append(append, "\", \"$scope\" : "); + bool result = mcommon_json_append_bson_document(append, scope, mode, max_depth); + (void)mcommon_string_append(append, " }"); + return result; +} + +bool +mcommon_json_append_value_symbol(mcommon_string_append_t *append, + const char *symbol, + uint32_t symbol_len, + bson_json_mode_t mode) +{ + if (mode == BSON_JSON_MODE_CANONICAL || mode == BSON_JSON_MODE_RELAXED) { + return mcommon_string_append(append, "{ \"$symbol\" : \"") && + mcommon_json_append_escaped(append, symbol, symbol_len, true) && mcommon_string_append(append, "\" }"); + } else { + return mcommon_json_append_value_utf8(append, symbol, symbol_len, true); + } +} diff --git a/bsonjs/common/common-macros-private.h b/bsonjs/common/common-macros-private.h index 360a0f5..068b094 100644 --- a/bsonjs/common/common-macros-private.h +++ b/bsonjs/common/common-macros-private.h @@ -1,15 +1,111 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -#include "common-prelude.h" +#include -#ifndef MONGO_C_DRIVER_COMMON_MACROS_H -#define MONGO_C_DRIVER_COMMON_MACROS_H +#ifndef MONGO_C_DRIVER_COMMON_MACROS_PRIVATE_H +#define MONGO_C_DRIVER_COMMON_MACROS_PRIVATE_H /* Test only assert. Is a noop unless -DENABLE_DEBUG_ASSERTIONS=ON is set * during configuration */ #if defined(MONGOC_ENABLE_DEBUG_ASSERTIONS) && defined(BSON_OS_UNIX) -#define MONGOC_DEBUG_ASSERT(statement) BSON_ASSERT (statement) +#define MONGOC_DEBUG_ASSERT(statement) BSON_ASSERT(statement) #else -#define MONGOC_DEBUG_ASSERT(statement) ((void) 0) +#define MONGOC_DEBUG_ASSERT(statement) ((void)0) #endif +#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) +#define MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") +#define MC_PRAGMA_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif defined(__clang__) +#define MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") +#define MC_PRAGMA_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif defined(_MSC_VER) +#define MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("warning ( push )") +#define MC_PRAGMA_DIAGNOSTIC_POP _Pragma("warning ( pop )") +#else +#define MC_PRAGMA_DIAGNOSTIC_PUSH +#define MC_PRAGMA_DIAGNOSTIC_POP +#endif + +// `MC_ENABLE_CONVERSION_WARNING_BEGIN` enables -Wconversion to check for potentially unsafe integer conversions. +// The `mcommon_in_range_*` functions can help address these warnings by ensuring a cast is within bounds. +#if defined(__GNUC__) +#define MC_ENABLE_CONVERSION_WARNING_BEGIN MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic warning \"-Wconversion\"") +#define MC_ENABLE_CONVERSION_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP +#elif defined(__clang__) +#define MC_ENABLE_CONVERSION_WARNING_BEGIN \ + MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("clang diagnostic warning \"-Wconversion\"") +#define MC_ENABLE_CONVERSION_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP +#else +#define MC_ENABLE_CONVERSION_WARNING_BEGIN +#define MC_ENABLE_CONVERSION_WARNING_END +#endif + +// Disable the -Wcast-function-type-strict warning. +#define MC_DISABLE_CAST_FUNCTION_TYPE_STRICT_WARNING_BEGIN +#define MC_DISABLE_CAST_FUNCTION_TYPE_STRICT_WARNING_END +#if defined(__clang__) +#if __has_warning("-Wcast-function-type-strict") +#undef MC_DISABLE_CAST_FUNCTION_TYPE_STRICT_WARNING_BEGIN +#undef MC_DISABLE_CAST_FUNCTION_TYPE_STRICT_WARNING_END +#define MC_DISABLE_CAST_FUNCTION_TYPE_STRICT_WARNING_BEGIN \ + MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("clang diagnostic ignored \"-Wcast-function-type-strict\"") +#define MC_DISABLE_CAST_FUNCTION_TYPE_STRICT_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP +#endif // __has_warning("-Wcast-function-type-strict") +#endif // defined(__clang__) + +#if defined(__GNUC__) +#define BEGIN_IGNORE_DEPRECATIONS \ + MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#define END_IGNORE_DEPRECATIONS MC_PRAGMA_DIAGNOSTIC_POP +#elif defined(__clang__) +#define BEGIN_IGNORE_DEPRECATIONS \ + MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#define END_IGNORE_DEPRECATIONS MC_PRAGMA_DIAGNOSTIC_PUSH +#else +#define BEGIN_IGNORE_DEPRECATIONS +#define END_IGNORE_DEPRECATIONS #endif + +// Disable the -Wimplicit warning (including -Wimplicit-int and -Wimplicit-function-declaration). +#if defined(__GNUC__) +#define MC_DISABLE_IMPLICIT_WARNING_BEGIN MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic ignored \"-Wimplicit\"") +#define MC_DISABLE_IMPLICIT_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP +#elif defined(__clang__) +#define MC_DISABLE_IMPLICIT_WARNING_BEGIN MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("clang diagnostic ignored \"-Wimplicit\"") +#define MC_DISABLE_IMPLICIT_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP +#elif defined(_MSC_VER) +#define MC_DISABLE_IMPLICIT_WARNING_BEGIN MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("warning (disable : 4013 4431)") +#define MC_DISABLE_IMPLICIT_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP +#else +#define MC_DISABLE_IMPLICIT_WARNING_BEGIN +#define MC_DISABLE_IMPLICIT_WARNING_END +#endif + +// Disable the -Wcast-qual warning +#if defined(__GNUC__) +#define MC_DISABLE_CAST_QUAL_WARNING_BEGIN MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#define MC_DISABLE_CAST_QUAL_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP +#elif defined(__clang__) +#define MC_DISABLE_CAST_QUAL_WARNING_BEGIN MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#define MC_DISABLE_CAST_QUAL_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP +#else +#define MC_DISABLE_CAST_QUAL_WARNING_BEGIN +#define MC_DISABLE_CAST_QUAL_WARNING_END +#endif + +#endif /* MONGO_C_DRIVER_COMMON_MACROS_PRIVATE_H */ diff --git a/bsonjs/common/common-md5-private.h b/bsonjs/common/common-md5-private.h index 4feadb9..2740a0e 100644 --- a/bsonjs/common/common-md5-private.h +++ b/bsonjs/common/common-md5-private.h @@ -1,5 +1,5 @@ /* - * Copyright 2018-present MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,26 +14,32 @@ * limitations under the License. */ -#include "common-prelude.h" +#include -#ifndef COMMON_MD5_PRIVATE_H -#define COMMON_MD5_PRIVATE_H +#ifndef MONGO_C_DRIVER_COMMON_MD5_PRIVATE_H +#define MONGO_C_DRIVER_COMMON_MD5_PRIVATE_H -#include "bson/bson.h" +#include BSON_BEGIN_DECLS -#define mcommon_md5_init COMMON_NAME (md5_init) -#define mcommon_md5_append COMMON_NAME (md5_append) -#define mcommon_md5_finish COMMON_NAME (md5_finish) +typedef struct { + uint32_t count[2]; /* message length in bits, lsw first */ + uint32_t abcd[4]; /* digest buffer */ + uint8_t buf[64]; /* accumulate block */ +} bson_md5_t; + +#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); +mcommon_md5_init(bson_md5_t *pms); void -mcommon_md5_append (bson_md5_t *pms, const uint8_t *data, uint32_t nbytes); +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]); +mcommon_md5_finish(bson_md5_t *pms, uint8_t digest[16]); BSON_END_DECLS -#endif /* COMMON_MD5_PRIVATE_H */ +#endif /* MONGO_C_DRIVER_COMMON_MD5_PRIVATE_H */ diff --git a/bsonjs/common/common-md5.c b/bsonjs/common/common-md5.c index 71fa989..05d59b1 100644 --- a/bsonjs/common/common-md5.c +++ b/bsonjs/common/common-md5.c @@ -56,9 +56,9 @@ * specified in libbson. */ -#include +#include -#include "common-md5-private.h" +#include #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #if BSON_BYTE_ORDER == BSON_BIG_ENDIAN @@ -67,7 +67,7 @@ #define BYTE_ORDER -1 #endif -#define T_MASK ((uint32_t) ~0) +#define T_MASK ((uint32_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db @@ -135,7 +135,7 @@ static void -bson_md5_process (bson_md5_t *md5, const uint8_t *data) +bson_md5_process(bson_md5_t *md5, const uint8_t *data) { uint32_t a = md5->abcd[0]; uint32_t b = md5->abcd[1]; @@ -161,7 +161,7 @@ bson_md5_process (bson_md5_t *md5, const uint8_t *data) */ static const int w = 1; - if (*((const uint8_t *) &w)) /* dynamic little-endian */ + if (*((const uint8_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { @@ -169,19 +169,19 @@ bson_md5_process (bson_md5_t *md5, const uint8_t *data) * On little-endian machines, we can process properly aligned * data without copying it. */ - if (!(((uintptr_t) data) & 3u)) { + if (!(((uintptr_t)data) & 3u)) { /* data are properly aligned */ #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-align" #endif - X = (const uint32_t *) data; + X = (const uint32_t *)data; #ifdef __clang__ #pragma clang diagnostic pop #endif } else { /* not aligned */ - memcpy (xbuf, data, sizeof (xbuf)); + memcpy(xbuf, data, sizeof(xbuf)); X = xbuf; } } @@ -215,104 +215,104 @@ bson_md5_process (bson_md5_t *md5, const uint8_t *data) /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) -#define SET(a, b, c, d, k, s, Ti) \ - t = a + F (b, c, d) + X[k] + Ti; \ - a = ROTATE_LEFT (t, s) + b +#define SET(a, b, c, d, k, s, Ti) \ + t = a + F(b, c, d) + X[k] + Ti; \ + a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ - SET (a, b, c, d, 0, 7, T1); - SET (d, a, b, c, 1, 12, T2); - SET (c, d, a, b, 2, 17, T3); - SET (b, c, d, a, 3, 22, T4); - SET (a, b, c, d, 4, 7, T5); - SET (d, a, b, c, 5, 12, T6); - SET (c, d, a, b, 6, 17, T7); - SET (b, c, d, a, 7, 22, T8); - SET (a, b, c, d, 8, 7, T9); - SET (d, a, b, c, 9, 12, T10); - SET (c, d, a, b, 10, 17, T11); - SET (b, c, d, a, 11, 22, T12); - SET (a, b, c, d, 12, 7, T13); - SET (d, a, b, c, 13, 12, T14); - SET (c, d, a, b, 14, 17, T15); - SET (b, c, d, a, 15, 22, T16); + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) -#define SET(a, b, c, d, k, s, Ti) \ - t = a + G (b, c, d) + X[k] + Ti; \ - a = ROTATE_LEFT (t, s) + b +#define SET(a, b, c, d, k, s, Ti) \ + t = a + G(b, c, d) + X[k] + Ti; \ + a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ - SET (a, b, c, d, 1, 5, T17); - SET (d, a, b, c, 6, 9, T18); - SET (c, d, a, b, 11, 14, T19); - SET (b, c, d, a, 0, 20, T20); - SET (a, b, c, d, 5, 5, T21); - SET (d, a, b, c, 10, 9, T22); - SET (c, d, a, b, 15, 14, T23); - SET (b, c, d, a, 4, 20, T24); - SET (a, b, c, d, 9, 5, T25); - SET (d, a, b, c, 14, 9, T26); - SET (c, d, a, b, 3, 14, T27); - SET (b, c, d, a, 8, 20, T28); - SET (a, b, c, d, 13, 5, T29); - SET (d, a, b, c, 2, 9, T30); - SET (c, d, a, b, 7, 14, T31); - SET (b, c, d, a, 12, 20, T32); + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) -#define SET(a, b, c, d, k, s, Ti) \ - t = a + H (b, c, d) + X[k] + Ti; \ - a = ROTATE_LEFT (t, s) + b +#define SET(a, b, c, d, k, s, Ti) \ + t = a + H(b, c, d) + X[k] + Ti; \ + a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ - SET (a, b, c, d, 5, 4, T33); - SET (d, a, b, c, 8, 11, T34); - SET (c, d, a, b, 11, 16, T35); - SET (b, c, d, a, 14, 23, T36); - SET (a, b, c, d, 1, 4, T37); - SET (d, a, b, c, 4, 11, T38); - SET (c, d, a, b, 7, 16, T39); - SET (b, c, d, a, 10, 23, T40); - SET (a, b, c, d, 13, 4, T41); - SET (d, a, b, c, 0, 11, T42); - SET (c, d, a, b, 3, 16, T43); - SET (b, c, d, a, 6, 23, T44); - SET (a, b, c, d, 9, 4, T45); - SET (d, a, b, c, 12, 11, T46); - SET (c, d, a, b, 15, 16, T47); - SET (b, c, d, a, 2, 23, T48); + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) -#define SET(a, b, c, d, k, s, Ti) \ - t = a + I (b, c, d) + X[k] + Ti; \ - a = ROTATE_LEFT (t, s) + b +#define SET(a, b, c, d, k, s, Ti) \ + t = a + I(b, c, d) + X[k] + Ti; \ + a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ - SET (a, b, c, d, 0, 6, T49); - SET (d, a, b, c, 7, 10, T50); - SET (c, d, a, b, 14, 15, T51); - SET (b, c, d, a, 5, 21, T52); - SET (a, b, c, d, 12, 6, T53); - SET (d, a, b, c, 3, 10, T54); - SET (c, d, a, b, 10, 15, T55); - SET (b, c, d, a, 1, 21, T56); - SET (a, b, c, d, 8, 6, T57); - SET (d, a, b, c, 15, 10, T58); - SET (c, d, a, b, 6, 15, T59); - SET (b, c, d, a, 13, 21, T60); - SET (a, b, c, d, 4, 6, T61); - SET (d, a, b, c, 11, 10, T62); - SET (c, d, a, b, 2, 15, T63); - SET (b, c, d, a, 9, 21, T64); + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each @@ -325,7 +325,7 @@ bson_md5_process (bson_md5_t *md5, const uint8_t *data) } void -mcommon_md5_init (bson_md5_t *pms) +mcommon_md5_init(bson_md5_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; @@ -335,12 +335,12 @@ mcommon_md5_init (bson_md5_t *pms) } void -mcommon_md5_append (bson_md5_t *pms, const uint8_t *data, uint32_t nbytes) +mcommon_md5_append(bson_md5_t *pms, const uint8_t *data, uint32_t nbytes) { const uint8_t *p = data; - int left = nbytes; - int offset = (pms->count[0] >> 3) & 63; - uint32_t nbits = (uint32_t) (nbytes << 3); + uint32_t left = nbytes; + uint8_t offset = (pms->count[0] >> 3) & 63; + uint32_t nbits = (uint32_t)(nbytes << 3); if (nbytes <= 0) return; @@ -353,28 +353,28 @@ mcommon_md5_append (bson_md5_t *pms, const uint8_t *data, uint32_t nbytes) /* Process an initial partial block. */ if (offset) { - int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + uint32_t copy = (offset + nbytes > 64u ? 64u - offset : nbytes); - memcpy (pms->buf + offset, p, copy); + memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; - bson_md5_process (pms, pms->buf); + bson_md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) - bson_md5_process (pms, p); + bson_md5_process(pms, p); /* Process a final partial block. */ if (left) - memcpy (pms->buf, p, left); + memcpy(pms->buf, p, left); } void -mcommon_md5_finish (bson_md5_t *pms, uint8_t digest[16]) +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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -384,11 +384,11 @@ mcommon_md5_finish (bson_md5_t *pms, uint8_t digest[16]) /* Save the length before padding. */ for (i = 0; i < 8; ++i) - data[i] = (uint8_t) (pms->count[i >> 2] >> ((i & 3) << 3)); + data[i] = (uint8_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ - mcommon_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. */ - mcommon_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)); + digest[i] = (uint8_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } diff --git a/bsonjs/common/common-oid-private.h b/bsonjs/common/common-oid-private.h new file mode 100644 index 0000000..ca6a075 --- /dev/null +++ b/bsonjs/common/common-oid-private.h @@ -0,0 +1,36 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef MONGO_C_DRIVER_COMMON_OID_PRIVATE_H +#define MONGO_C_DRIVER_COMMON_OID_PRIVATE_H + +#include + +BSON_BEGIN_DECLS + +extern const bson_oid_t kZeroObjectId; + +void +mcommon_oid_set_zero(bson_oid_t *oid); + +bool +mcommon_oid_is_zero(const bson_oid_t *oid); + +BSON_END_DECLS + +#endif /* MONGO_C_DRIVER_COMMON_OID_PRIVATE_H */ diff --git a/bsonjs/common/common-oid.c b/bsonjs/common/common-oid.c new file mode 100644 index 0000000..21d3c5c --- /dev/null +++ b/bsonjs/common/common-oid.c @@ -0,0 +1,33 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +const bson_oid_t kZeroObjectId = {{0}}; + +void +mcommon_oid_set_zero(bson_oid_t *oid) +{ + BSON_ASSERT(oid); + memset(oid, 0, sizeof *oid); +} + +bool +mcommon_oid_is_zero(const bson_oid_t *oid) +{ + BSON_ASSERT(oid); + return bson_oid_equal_unsafe(oid, &kZeroObjectId); +} diff --git a/bsonjs/common/common-prelude.h b/bsonjs/common/common-prelude.h index 4f3a5fd..62eabd4 100644 --- a/bsonjs/common/common-prelude.h +++ b/bsonjs/common/common-prelude.h @@ -1,5 +1,5 @@ /* - * Copyright 2018-present MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,11 @@ #error "Only or can be included directly." #endif -#define COMMON_NAME_1(a, b) COMMON_NAME_2 (a, b) +#define COMMON_NAME_1(a, b) COMMON_NAME_2(a, b) #define COMMON_NAME_2(a, b) a##_##b #if defined(MCOMMON_NAME_PREFIX) && !defined(__INTELLISENSE__) -#define COMMON_NAME(Name) COMMON_NAME_1 (MCOMMON_NAME_PREFIX, Name) +#define COMMON_NAME(Name) COMMON_NAME_1(MCOMMON_NAME_PREFIX, Name) #else -#define COMMON_NAME(Name) COMMON_NAME_1 (mcommon, Name) +#define COMMON_NAME(Name) COMMON_NAME_1(mcommon, Name) #endif diff --git a/bsonjs/common/common-string-private.h b/bsonjs/common/common-string-private.h new file mode 100644 index 0000000..8f9e8b1 --- /dev/null +++ b/bsonjs/common/common-string-private.h @@ -0,0 +1,673 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef MONGO_C_DRIVER_COMMON_STRING_PRIVATE_H +#define MONGO_C_DRIVER_COMMON_STRING_PRIVATE_H + +#include + +#include + +#include + + +/* + * In mcommon_string_t, 'str' is guaranteed to be NUL terminated and SHOULD be valid UTF-8. mcommon_string_t operations + * MUST maintain the validity of valid UTF-8 strings. + * + * Unused portions of the buffer may be uninitialized, and must not be compared or copied. + * + * 'len' is measured in bytes, not including the NUL terminator. + * + * 'alloc' is the actual length of the bson_malloc() allocation in bytes, including the required space for NUL + * termination. + * + * When we use 'capacity', it refers to the largest 'len' that the buffer could store. alloc == capacity + 1. + */ +typedef struct mcommon_string_t { + char *str; + uint32_t len; + uint32_t alloc; +} mcommon_string_t; + +/* Parameters and outcome for a bounded append operation on a mcommon_string_t. Individual type-specific append + * functions can consume this struct to communicate bounds info. "max_len_exceeded" can be tested any time an + * algorithmic exit is convenient; the actual appended content will be limited by max_len. Truncation is guaranteed not + * to split a valid UTF-8 byte sequence. + * + * Members are here to support inline definitions; not intended for direct access. + * + * Multiple mcommon_string_append_t may simultaneously refer to the same 'string' but this usage is not recommended. + * + * 'max_len_exceeded' only includes operations undertaken on this specific mcommon_string_append_t. It will not be set + * if the string was already overlong, or if a different mcommon_string_append_t experiences an overage. + */ +typedef struct mcommon_string_append_t { + mcommon_string_t *_string; + uint32_t _max_len; + bool _max_len_exceeded; +} mcommon_string_append_t; + +#define mcommon_string_new_with_capacity COMMON_NAME(string_new_with_capacity) +#define mcommon_string_new_with_buffer COMMON_NAME(string_new_with_buffer) +#define mcommon_string_destroy COMMON_NAME(string_destroy) +#define mcommon_string_destroy_with_steal COMMON_NAME(string_destroy_with_steal) +#define mcommon_string_grow_to_capacity COMMON_NAME(string_grow_to_capacity) +#define mcommon_string_append_selected_chars COMMON_NAME(string_append_selected_chars) +#define mcommon_string_append_bytes_internal COMMON_NAME(string_append_bytes_internal) +#define mcommon_string_append_bytes_all_or_none COMMON_NAME(string_append_bytes_all_or_none) +#define mcommon_string_append_unichar_internal COMMON_NAME(string_append_unichar_internal) +#define mcommon_string_append_base64_encode COMMON_NAME(string_append_base64_encode) +#define mcommon_string_append_oid_as_hex COMMON_NAME(string_append_oid_as_hex) +#define mcommon_string_append_printf COMMON_NAME(string_append_printf) +#define mcommon_string_append_vprintf COMMON_NAME(string_append_vprintf) + +bool +mcommon_string_append_bytes_internal(mcommon_string_append_t *append, const char *str, uint32_t len); + +bool +mcommon_string_append_unichar_internal(mcommon_string_append_t *append, bson_unichar_t unichar); + +/** + * @brief Allocate a new mcommon_string_t with a copy of the supplied initializer string and an explicit buffer + * capacity. + * + * @param str Initializer string, should be valid UTF-8. + * @param length Length of initializer string, in bytes. + * @param min_capacity Minimum string capacity, in bytes, the buffer must be able to store without reallocating. Does + * not include the NUL terminator. Must be less than UINT32_MAX. + * @returns A new mcommon_string_t that must be freed with mcommon_string_destroy() or + * mcommon_string_destroy_with_steal() and bson_free(). It will hold 'str' in its entirety, even if the requested + * min_capacity was smaller. + */ +mcommon_string_t * +mcommon_string_new_with_capacity(const char *str, uint32_t length, uint32_t min_capacity); + +/** + * @brief Allocate a new mcommon_string_t with a copy of the supplied initializer string and a minimum-capacity buffer + * + * @param str NUL terminated string, should be valid UTF-8. Must be less than UINT32_MAX bytes long, overlong input + * causes a runtime assertion failure. + * @returns A new mcommon_string_t that must be freed with mcommon_string_destroy() or + * mcommon_string_destroy_with_steal() and bson_free(). + */ +static BSON_INLINE mcommon_string_t * +mcommon_string_new(const char *str) +{ + BSON_ASSERT_PARAM(str); + size_t length = strlen(str); + BSON_ASSERT(mlib_in_range(uint32_t, length) && (uint32_t)length < UINT32_MAX); + return mcommon_string_new_with_capacity(str, (uint32_t)length, 0); +} + +/** + * @brief Allocate a new mcommon_string_t, taking ownership of an existing buffer + * + * @param buffer Buffer to adopt, suitable for bson_free() and bson_realloc(). + * @param length Length of the string data, in bytes, not including the required NUL terminator. If string data is + * present, it should be valid UTF-8. + * @param alloc Actual allocated size of the buffer, in bytes, including room for NUL termination. + * @returns A new mcommon_string_t that must be freed with mcommon_string_destroy() or + * mcommon_string_destroy_with_steal() and bson_free(). + */ +mcommon_string_t * +mcommon_string_new_with_buffer(char *buffer, uint32_t length, uint32_t alloc); + +/** + * @brief Deallocate a mcommon_string_t and its internal buffer + * @param string String allocated with mcommon_string_new, or NULL. + */ +void +mcommon_string_destroy(mcommon_string_t *string); + +/** + * @brief Deallocate a mcommon_string_t and return its internal buffer as a NUL-terminated C string. + * @param string String allocated with mcommon_string_new, or NULL. + * @returns A freestanding NUL-terminated string in a buffer that must be freed with bson_free(), or NULL if 'string' + * was NULL. + */ +char * +mcommon_string_destroy_with_steal(mcommon_string_t *string); + +/** + * @brief Truncate the string to zero length without deallocating the buffer + * @param string String to clear + */ +static BSON_INLINE void +mcommon_string_clear(mcommon_string_t *string) +{ + BSON_ASSERT_PARAM(string); + string->len = 0; + string->str[0] = '\0'; +} + +/** + * @brief Test if the string has zero length + * @param string String to test + */ +static BSON_INLINE bool +mcommon_string_is_empty(const mcommon_string_t *string) +{ + BSON_ASSERT_PARAM(string); + return string->len == 0; +} + +/** + * @brief Test if the string begins with a C string + * @param string mcommon_string_t to test + * @param substring prefix to match, as a NUL terminated C string. + */ +static BSON_INLINE bool +mcommon_string_starts_with_str(const mcommon_string_t *string, const char *substring) +{ + BSON_ASSERT_PARAM(string); + BSON_ASSERT_PARAM(substring); + + size_t substring_len = strlen(substring); + uint32_t string_len = string->len; + + if (mlib_in_range(uint32_t, substring_len) && (uint32_t)substring_len <= string_len) { + return 0 == memcmp(string->str, substring, substring_len); + } else { + return false; + } +} + +/** + * @brief Test if the string ends with a C string + * @param string mcommon_string_t to test + * @param substring suffix to match, as a NUL terminated C string. + */ +static BSON_INLINE bool +mcommon_string_ends_with_str(const mcommon_string_t *string, const char *substring) +{ + BSON_ASSERT_PARAM(string); + BSON_ASSERT_PARAM(substring); + + size_t substring_len = strlen(substring); + uint32_t string_len = string->len; + + if (mlib_in_range(uint32_t, substring_len) && (uint32_t)substring_len <= string_len) { + uint32_t offset = string_len - (uint32_t)substring_len; + return 0 == memcmp(string->str + offset, substring, substring_len); + } else { + return false; + } +} + +/** + * @brief Grow a mcommon_string_t buffer if necessary to ensure a minimum capacity + * + * @param string String allocated with mcommon_string_new + * @param capacity Minimum string length, in bytes, the buffer must be able to store without reallocating. Does not + * include the NUL terminator. Must be less than UINT32_MAX. + * + * If a reallocation is necessary, the actual allocation size will be chosen as the next highest power-of-two above the + * minimum needed to store 'capacity' as well as the NUL terminator. + */ +void +mcommon_string_grow_to_capacity(mcommon_string_t *string, uint32_t capacity); + +/** + * @brief Set an append operation for this string, with an explicit length limit + * @param string String allocated with mcommon_string_new + * @param new_append Pointer to an uninitialized mcommon_string_append_t + * @param max_len Maximum allowed length for the resulting string, in bytes. Must be less than UINT32_MAX. + * + * The mcommon_string_append_t does not need to be deallocated. It is no longer usable if the underlying + * mcommon_string_t is freed. + * + * If the string was already over maximum length, it will not be modified. All append operations are guaranteed not to + * lengthen the string beyond max_len. Truncations are guaranteed to happen at UTF-8 code point boundaries. + */ +static BSON_INLINE void +mcommon_string_set_append_with_limit(mcommon_string_t *string, mcommon_string_append_t *new_append, uint32_t max_len) +{ + BSON_ASSERT_PARAM(string); + BSON_ASSERT_PARAM(new_append); + BSON_ASSERT(max_len < UINT32_MAX); + + new_append->_string = string; + new_append->_max_len = max_len; + new_append->_max_len_exceeded = false; +} + +/** + * @brief Set an append operation for this string + * @param string String allocated with mcommon_string_new + * @param new_append Pointer to an uninitialized mcommon_string_append_t + * + * The mcommon_string_append_t does not need to be deallocated. It is no longer usable if the underlying + * mcommon_string_t is freed. + * + * The maximum string length will be set to the largest representable by the data type, UINT32_MAX - 1. + */ +static BSON_INLINE void +mcommon_string_set_append(mcommon_string_t *string, mcommon_string_append_t *new_append) +{ + BSON_ASSERT_PARAM(string); + BSON_ASSERT_PARAM(new_append); + + mcommon_string_set_append_with_limit(string, new_append, UINT32_MAX - 1u); +} + +/** + * @brief Allocate an empty mcommon_string_t with the specified initial capacity, and set an append operation for it + * with maximum length + * @param new_append Pointer to an uninitialized mcommon_string_append_t + * @param capacity Initial capacity for the string, in bytes, not including NUL termination + * + * Allocates a new mcommon_string_t, which will need to be deallocated by the caller. + * The mcommon_string_append_t itself does not need to be deallocated. + * + * The initial mcommon_string_t buffer will be allocated to have room for the given number of string bytes, not + * including the NUL terminator. The maximum append length will be set to the largest representable by the data type, + * UINT32_MAX - 1. + * + * This is a shortcut for mcommon_string_new_with_capacity() combined with mcommon_string_set_append(). + */ +static BSON_INLINE void +mcommon_string_new_with_capacity_as_append(mcommon_string_append_t *new_append, uint32_t capacity) +{ + BSON_ASSERT_PARAM(new_append); + + mcommon_string_set_append(mcommon_string_new_with_capacity("", 0, capacity), new_append); +} + +/** + * @brief Allocate an empty mcommon_string_t with default initial capacity, and set an append operation for it with + * maximum length + * @param new_append Pointer to an uninitialized mcommon_string_append_t + * + * Allocates a new mcommon_string_t, which will need to be deallocated by the caller. + * The mcommon_string_append_t itself does not need to be deallocated. + * + * The maximum string length will be set to the largest representable by the data type, UINT32_MAX - 1. + * The new string will be allocated with a small default capacity. + * + * This method is intended to be the most convenient way to start growing a string. If a reasonable guess + * can be made about the final size of the string, it's better to call mcommon_string_new_with_capacity_as_append() + * or mcommon_string_new_with_capacity() and mcommon_string_set_append(). + */ +static BSON_INLINE void +mcommon_string_new_as_append(mcommon_string_append_t *new_append) +{ + BSON_ASSERT_PARAM(new_append); + + mcommon_string_new_with_capacity_as_append(new_append, 32); +} + +/** + * @brief Begin appending to a new empty mcommon_string_t with a given capacity and a matching max append length. + * @param new_append Pointer to an uninitialized mcommon_string_append_t + * @param capacity Fixed capacity for the string, in bytes, not including NUL termination + * + * Allocates a new mcommon_string_t, which will need to be deallocated by the caller. + * The mcommon_string_append_t itself does not need to be deallocated. + * The string buffer will not need to resize for operations performed through the resulting mcommon_string_append_t. + */ +static BSON_INLINE void +mcommon_string_new_as_fixed_capacity_append(mcommon_string_append_t *new_append, uint32_t capacity) +{ + BSON_ASSERT_PARAM(new_append); + + mcommon_string_set_append_with_limit(mcommon_string_new_with_capacity("", 0, capacity), new_append, capacity); +} + +/** + * @brief Check the status of an append operation. + * @param append Append operation, initialized with mcommon_string_set_append + * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. + */ +static BSON_INLINE bool +mcommon_string_status_from_append(const mcommon_string_append_t *append) +{ + BSON_ASSERT_PARAM(append); + + return !append->_max_len_exceeded; +} + +/** + * @brief Get a mcommon_string_t pointer to a mcommon_string_append_t destination. + * @param append Append operation, initialized with mcommon_string_set_append + * @returns Pointer to the mcommon_string_t destination. + * + * The mcommon_string_append_t includes a plain mcommon_string_t pointer with no fixed ownership semantics. + * Depending on usage, it may be a string with borrowed ownership or the append operation may be its primary owner. + */ +static BSON_INLINE mcommon_string_t * +mcommon_string_from_append(const mcommon_string_append_t *append) +{ + BSON_ASSERT_PARAM(append); + + return append->_string; +} + +/** + * @brief Get the current string buffer for an mcommon_string_append_t destination. + * @param append Append operation, initialized with mcommon_string_set_append + * @returns String buffer pointer, NUL terminated, invalidated if the string is destroyed and by any operation that may + * grow the string. + * + * Shortcut for mcommon_string_from_append(append)->str + */ +static BSON_INLINE char * +mcommon_str_from_append(const mcommon_string_append_t *append) +{ + BSON_ASSERT_PARAM(append); + + return mcommon_string_from_append(append)->str; +} + +/** + * @brief Get the current string length for an mcommon_string_append_t destination. + * @param append Append operation, initialized with mcommon_string_set_append + * @returns Snapshot of the current string length + * + * Shortcut for mcommon_string_from_append(append)->len + */ +static BSON_INLINE uint32_t +mcommon_strlen_from_append(const mcommon_string_append_t *append) +{ + BSON_ASSERT_PARAM(append); + + return mcommon_string_from_append(append)->len; +} + +/** + * @brief Deallocate the mcommon_string_t destination associated with an mcommon_string_append_t + * @param append Append operation, initialized with mcommon_string_set_append + * The append operation will no longer be usable after this call. + */ +static BSON_INLINE void +mcommon_string_from_append_destroy(const mcommon_string_append_t *append) +{ + BSON_ASSERT_PARAM(append); + + mcommon_string_destroy(mcommon_string_from_append(append)); +} + +/** + * @brief Truncate the append destination string to zero length without deallocating its buffer. + * @param append Append operation, initialized with mcommon_string_set_append + * This is equivalent to mcommon_string_clear() combined with mcommon_string_from_append(). + */ +static BSON_INLINE void +mcommon_string_from_append_clear(const mcommon_string_append_t *append) +{ + BSON_ASSERT_PARAM(append); + + mcommon_string_clear(mcommon_string_from_append(append)); +} + +/** + * @brief Deallocate the mcommon_string_t destination associated with an mcommon_string_append_t and return its internal + * buffer + * @param append Append operation, initialized with mcommon_string_set_append + * @returns A freestanding NUL-terminated string in a buffer that must be freed with bson_free() + * The append operation will no longer be usable after this call. + */ +static BSON_INLINE char * +mcommon_string_from_append_destroy_with_steal(const mcommon_string_append_t *append) +{ + BSON_ASSERT_PARAM(append); + + return mcommon_string_destroy_with_steal(mcommon_string_from_append(append)); +} + +/** + * @brief Test if the append destination ends with a C string + * @param string mcommon_string_append_t with the string to test + * @param substring suffix to match, as a NUL terminated C string. + */ +static BSON_INLINE bool +mcommon_string_from_append_ends_with_str(const mcommon_string_append_t *append, const char *substring) +{ + BSON_ASSERT_PARAM(append); + BSON_ASSERT_PARAM(substring); + + return mcommon_string_ends_with_str(mcommon_string_from_append(append), substring); +} + +/** + * @brief Test if the append destination has zero length + * @param string mcommon_string_append_t with the string to test + */ +static BSON_INLINE bool +mcommon_string_from_append_is_empty(const mcommon_string_append_t *append) +{ + BSON_ASSERT_PARAM(append); + + return mcommon_string_is_empty(mcommon_string_from_append(append)); +} + +/** + * @brief Signal an explicit overflow during string append + * @param append Append operation, initialized with mcommon_string_set_append + * + * Future calls to mcommon_string_status_from_append() return false, exactly as if an overlong append was attempted and + * failed. This should be used for cases when a logical overflow is occurring but it was detected early enough that no + * actual append was attempted. + */ +static BSON_INLINE void +mcommon_string_append_overflow(mcommon_string_append_t *append) +{ + BSON_ASSERT_PARAM(append); + + append->_max_len_exceeded = true; +} + +/** + * @brief Append selected characters from a template + * @param append Append operation, initialized with mcommon_string_set_append + * @param template UTF-8 string listing allowed characters in the desired order + * @param selector UTF-8 string that chooses which template characters are appended + * @param selector_len Length of the selector string, in bytes + * + * Sort and filter lists of option characters. The template should list all allowed options in their desired order. + * This implementation does not support multi-byte template characters. ASSERTs that each template character is <= + * '\x7f'. Selectors may contain untrusted data, template should not. + */ +bool +mcommon_string_append_selected_chars(mcommon_string_append_t *append, + const char *template_, + const char *selector, + size_t selector_len); + +/** + * @brief Append a string with known length to the mcommon_string_t + * @param append Append operation, initialized with mcommon_string_set_append + * @param str String to append a copy of, should be valid UTF-8 + * @param len Length of 'str', in bytes + * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. + * + * If the string must be truncated to fit in the limit set by mcommon_string_set_append_with_limit, it will always be + * split in-between UTF-8 code points. + */ +static BSON_INLINE bool +mcommon_string_append_bytes(mcommon_string_append_t *append, const char *str, uint32_t len) +{ + BSON_ASSERT_PARAM(append); + BSON_ASSERT_PARAM(str); + + if (BSON_UNLIKELY(!mcommon_string_status_from_append(append))) { + return false; + } + + mcommon_string_t *string = append->_string; + char *buffer = string->str; + uint64_t alloc = (uint64_t)string->alloc; + uint64_t old_len = (uint64_t)string->len; + uint64_t max_len = (uint64_t)append->_max_len; + uint64_t new_len = old_len + (uint64_t)len; + uint64_t new_len_with_nul = new_len + 1; + + // Fast path: no truncation, no buffer growing + if (BSON_LIKELY(new_len <= max_len && new_len_with_nul <= alloc)) { + memcpy(buffer + old_len, str, len); + buffer[new_len] = '\0'; + string->len = (uint32_t)new_len; + return true; + } + + // Other cases are not inlined + return mcommon_string_append_bytes_internal(append, str, len); +} + +/** + * @brief Append a NUL-terminated UTF-8 string to the mcommon_string_t + * @param append Append operation, initialized with mcommon_string_set_append + * @param str NUL-terminated string to append a copy of + * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. + * + * If the string must be truncated to fit in the limit set by mcommon_string_set_append_with_limit, it will always be + * split in-between UTF-8 code points. + */ +static BSON_INLINE bool +mcommon_string_append(mcommon_string_append_t *append, const char *str) +{ + BSON_ASSERT_PARAM(append); + BSON_ASSERT_PARAM(str); + + return mcommon_string_append_bytes(append, str, strlen(str)); +} + +/** + * @brief Append an entire string with known length to the mcommon_string_t or fail, without truncating. + * @param append Append operation, initialized with mcommon_string_set_append + * @param str UTF-8 string to append a copy of + * @param len Length of 'str', in bytes + * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. + * + * Atomic version of mcommon_string_append_bytes. If string does not fit completely, it is not truncated. + * The destination string is only modified if the entire append operation can be completed. + */ +bool +mcommon_string_append_bytes_all_or_none(mcommon_string_append_t *append, const char *str, uint32_t len); + +/** + * @brief Append an entire NUL-terminated UTF-8 string to the mcommon_string_t or fail, without truncating. + * @param append Append operation, initialized with mcommon_string_set_append + * @param str NUL-terminated UTF-8 sequence to append a copy of + * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. + * + * Atomic version of mcommon_string_append. If string does not fit completely, it is not truncated. + * The destination string is only modified if the entire append operation can be completed. + */ +static BSON_INLINE bool +mcommon_string_append_all_or_none(mcommon_string_append_t *append, const char *str) +{ + BSON_ASSERT_PARAM(append); + BSON_ASSERT_PARAM(str); + + return mcommon_string_append_bytes_all_or_none(append, str, strlen(str)); +} + +/** + * @brief Append base64 encoded bytes to an mcommon_string_t + * @param append Append operation, initialized with mcommon_string_set_append + * @param bytes Bytes to be encoded + * @param len Number of bytes to encoded + * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. + */ +bool +mcommon_string_append_base64_encode(mcommon_string_append_t *append, const uint8_t *bytes, uint32_t len); + +/** + * @brief Append an ObjectId as a hex string + * @param append Append operation, initialized with mcommon_string_set_append + * @param value bson_oid_t value to copy + * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. + */ +bool +mcommon_string_append_oid_as_hex(mcommon_string_append_t *append, const bson_oid_t *value); + +/** + * @brief Append printf() formatted text to a mcommon_string_t + * @param append Append operation, initialized with mcommon_string_set_append + * @param format printf() format string + * @param ... Format string arguments + * @returns true if the append operation has no permanent error status, and this operation has succeeded. false if the + * max length has been surpassed or this printf() experienced an unrecoverable error. + * + * Writes the printf() result directly into the mcommon_string_t buffer, growing it as needed. + * + * If the string must be truncated to fit in the limit set by mcommon_string_set_append_with_limit, it will always be + * split in-between UTF-8 code points. + */ +bool +mcommon_string_append_printf(mcommon_string_append_t *append, const char *format, ...) BSON_GNUC_PRINTF(2, 3); + +/** + * @brief Variant of mcommon_string_append_printf() that takes a va_list + * @param append Append operation, initialized with mcommon_string_set_append + * @param format printf() format string + * @param args Format string arguments + * @returns true if the append operation has no permanent error status, and this operation has succeeded. false if the + * max length has been surpassed or this printf() experienced an unrecoverable error. + * + * Writes the printf() result directly into the mcommon_string_t buffer, growing it as needed. + * + * If the string must be truncated to fit in the limit set by mcommon_string_set_append_with_limit, it will always be + * split in-between UTF-8 code points. + */ +bool +mcommon_string_append_vprintf(mcommon_string_append_t *append, const char *format, va_list args) BSON_GNUC_PRINTF(2, 0); + +/** + * @brief Append one code point to a mcommon_string_t + * @param append Append operation, initialized with mcommon_string_set_append + * @param unichar Code point to append, as a bson_unichar_t + * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. + * + * Guaranteed not to truncate. The character will fully append or no change will be made. + */ +static BSON_INLINE bool +mcommon_string_append_unichar(mcommon_string_append_t *append, bson_unichar_t unichar) +{ + BSON_ASSERT_PARAM(append); + + if (BSON_UNLIKELY(!mcommon_string_status_from_append(append))) { + return false; + } + + mcommon_string_t *string = append->_string; + BSON_ASSERT(string); + char *buffer = string->str; + uint64_t alloc = (uint64_t)string->alloc; + uint64_t old_len = (uint64_t)string->len; + uint64_t max_len = (uint64_t)append->_max_len; + + // Fast path: single-byte character, no truncation, no buffer growing + if (BSON_LIKELY(unichar <= 0x7f)) { + uint64_t new_len = old_len + 1; + uint64_t new_len_with_nul = new_len + 1; + if (BSON_LIKELY(new_len <= max_len && new_len_with_nul <= alloc)) { + buffer[old_len] = (char)unichar; + buffer[new_len] = '\0'; + string->len = new_len; + return true; + } + } + + // Other cases are not inlined + return mcommon_string_append_unichar_internal(append, unichar); +} + + +#endif /* MONGO_C_DRIVER_COMMON_STRING_PRIVATE_H */ diff --git a/bsonjs/common/common-string.c b/bsonjs/common/common-string.c new file mode 100644 index 0000000..8aae2a0 --- /dev/null +++ b/bsonjs/common/common-string.c @@ -0,0 +1,372 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include + + +mcommon_string_t * +mcommon_string_new_with_capacity(const char *str, uint32_t length, uint32_t min_capacity) +{ + BSON_ASSERT_PARAM(str); + BSON_ASSERT(length < UINT32_MAX && min_capacity < UINT32_MAX); + uint32_t capacity = BSON_MAX(length, min_capacity); + uint32_t alloc = capacity + 1u; + char *buffer = bson_malloc(alloc); + memcpy(buffer, str, length); + buffer[length] = '\0'; + return mcommon_string_new_with_buffer(buffer, length, alloc); +} + +mcommon_string_t * +mcommon_string_new_with_buffer(char *buffer, uint32_t length, uint32_t alloc) +{ + BSON_ASSERT_PARAM(buffer); + BSON_ASSERT(length < UINT32_MAX && alloc >= length + 1u); + BSON_ASSERT(buffer[length] == '\0'); + mcommon_string_t *string = bson_malloc0(sizeof *string); + string->str = buffer; + string->len = length; + string->alloc = alloc; + return string; +} + +void +mcommon_string_destroy(mcommon_string_t *string) +{ + if (string) { + bson_free(mcommon_string_destroy_with_steal(string)); + } +} + +char * +mcommon_string_destroy_with_steal(mcommon_string_t *string) +{ + if (string) { + char *buffer = string->str; + BSON_ASSERT(buffer[string->len] == '\0'); + bson_free(string); + return buffer; + } else { + return NULL; + } +} + +void +mcommon_string_grow_to_capacity(mcommon_string_t *string, uint32_t capacity) +{ + BSON_ASSERT_PARAM(string); + BSON_ASSERT(capacity < UINT32_MAX); + uint32_t min_alloc_needed = capacity + 1u; + if (string->alloc < min_alloc_needed) { + uint32_t alloc = mcommon_next_power_of_two_u32(min_alloc_needed); + string->str = bson_realloc(string->str, alloc); + string->alloc = alloc; + } +} + +// Handle cases omitted from the inlined mcommon_string_append_bytes() +bool +mcommon_string_append_bytes_internal(mcommon_string_append_t *append, const char *str, uint32_t len) +{ + mcommon_string_t *string = append->_string; + BSON_ASSERT(string); + uint32_t old_len = string->len; + uint32_t max_len = append->_max_len; + BSON_ASSERT(max_len < UINT32_MAX); + + uint32_t max_append_len = old_len < max_len ? max_len - old_len : 0; + uint32_t truncated_append_len = len; + if (len > max_append_len) { + // Search for an actual append length, <= the maximum allowed, which preserves UTF-8 validity + append->_max_len_exceeded = true; + truncated_append_len = mcommon_utf8_truncate_len(str, max_append_len); + } + + uint32_t new_len = old_len + truncated_append_len; + BSON_ASSERT(new_len <= max_len); + mcommon_string_grow_to_capacity(string, new_len); + char *buffer = string->str; + + memcpy(buffer + old_len, str, truncated_append_len); + buffer[new_len] = '\0'; + string->len = new_len; + + return mcommon_string_status_from_append(append); +} + +// Variant of mcommon_string_append_bytes() that grows but never truncates +bool +mcommon_string_append_bytes_all_or_none(mcommon_string_append_t *append, const char *str, uint32_t len) +{ + BSON_ASSERT_PARAM(append); + BSON_ASSERT_PARAM(str); + + if (BSON_UNLIKELY(!mcommon_string_status_from_append(append))) { + return false; + } + + mcommon_string_t *string = append->_string; + BSON_ASSERT(string); + uint32_t old_len = string->len; + uint32_t max_len = append->_max_len; + BSON_ASSERT(max_len < UINT32_MAX); + + uint32_t max_append_len = old_len < max_len ? max_len - old_len : 0; + if (len > max_append_len) { + append->_max_len_exceeded = true; + return false; + } + + uint32_t new_len = old_len + len; + BSON_ASSERT(new_len <= max_len); + mcommon_string_grow_to_capacity(string, new_len); + char *buffer = string->str; + + memcpy(buffer + old_len, str, len); + buffer[new_len] = '\0'; + string->len = new_len; + + return mcommon_string_status_from_append(append); +} + +bool +mcommon_string_append_unichar_internal(mcommon_string_append_t *append, bson_unichar_t unichar) +{ + mcommon_string_t *string = append->_string; + uint32_t old_len = string->len; + uint32_t max_len = append->_max_len; + BSON_ASSERT(max_len < UINT32_MAX); + + char max_utf8_sequence[6]; + uint32_t max_append_len = old_len < max_len ? max_len - old_len : 0; + + // Usually we can write the UTF-8 sequence directly + if (BSON_LIKELY(max_append_len >= sizeof max_utf8_sequence)) { + uint32_t actual_sequence_len; + mcommon_string_grow_to_capacity(string, old_len + sizeof max_utf8_sequence); + char *buffer = string->str; + mcommon_utf8_from_unichar(unichar, buffer + old_len, &actual_sequence_len); + BSON_ASSERT(actual_sequence_len <= sizeof max_utf8_sequence); + BSON_ASSERT(append->_max_len_exceeded == false); + uint32_t new_len = old_len + actual_sequence_len; + buffer[new_len] = '\0'; + string->len = new_len; + return true; + } + + // If we are near max_len, avoid growing the buffer beyond it. + uint32_t actual_sequence_len; + mcommon_utf8_from_unichar(unichar, max_utf8_sequence, &actual_sequence_len); + return mcommon_string_append_bytes_internal(append, max_utf8_sequence, actual_sequence_len); +} + +bool +mcommon_string_append_base64_encode(mcommon_string_append_t *append, const uint8_t *bytes, uint32_t len) +{ + BSON_ASSERT_PARAM(append); + BSON_ASSERT_PARAM(bytes); + + if (BSON_UNLIKELY(!mcommon_string_status_from_append(append))) { + return false; + } + + mcommon_string_t *string = append->_string; + uint32_t old_len = string->len; + uint32_t max_len = append->_max_len; + BSON_ASSERT(max_len < UINT32_MAX); + uint32_t max_append_len = old_len < max_len ? max_len - old_len : 0; + + // Note that mcommon_b64_ntop_calculate_target_size includes room for NUL. + // mcommon_b64_ntop includes NUL in the input (buffer size) but not in the return value (string length). + size_t encoded_target_len = mcommon_b64_ntop_calculate_target_size((size_t)len) - 1; + + if (encoded_target_len <= (size_t)max_append_len) { + // No truncation needed. Grow the buffer and encode directly. + mcommon_string_grow_to_capacity(string, old_len + encoded_target_len); + const int tgt = mcommon_b64_ntop(bytes, (size_t)len, string->str + old_len, encoded_target_len + 1); + BSON_ASSERT(mlib_cmp(encoded_target_len, ==, tgt)); + BSON_ASSERT(mlib_in_range(uint32_t, encoded_target_len)); + string->len = old_len + (uint32_t)encoded_target_len; + return true; + } else if (max_append_len == 0) { + // Truncation to a zero-length append + mcommon_string_append_overflow(append); + return false; + } else { + /* We expect to append at least one byte, and truncate. + * Encoding only produces single-byte UTF-8 sequences, so the result always has exactly the maximum length. + * + * mcommon_b64_ntop() can't truncate without failing. To do this without allocating a full size temporary buffer + * or rewriting mcommon_b64_ntop, we can partition the write into three parts: a 'direct' portion made from entire + * non-truncated units of 3 bytes in and 4 characters out, a truncated 'remainder', and an ignored portion. + * Remainders longer than 3 bytes in / 4 bytes out are never necessary, and further portions of the input data + * will not be used. + */ + mcommon_string_grow_to_capacity(string, max_len); + char *buffer = string->str; + + uint32_t remainder_truncated_len = max_append_len % 4; + uint32_t direct_encoded_len = max_append_len - remainder_truncated_len; + uint32_t direct_input_len = mcommon_b64_pton_calculate_target_size((size_t)direct_encoded_len); + BSON_ASSERT(direct_input_len % 3 == 0); + BSON_ASSERT(direct_input_len < len); + const int tgt = mcommon_b64_ntop(bytes, (size_t)direct_input_len, string->str + old_len, direct_encoded_len + 1); + BSON_ASSERT(mlib_cmp(direct_encoded_len, ==, tgt)); + + char remainder_buffer[5]; + uint32_t remainder_input_len = BSON_MIN(3, len - direct_input_len); + BSON_ASSERT(remainder_input_len > 0); + uint32_t remainder_encoded_len = mcommon_b64_ntop_calculate_target_size((size_t)remainder_input_len) - 1; + BSON_ASSERT(remainder_encoded_len > remainder_truncated_len); + const int t2 = mcommon_b64_ntop( + bytes + direct_input_len, (size_t)remainder_input_len, remainder_buffer, sizeof remainder_buffer); + BSON_ASSERT(mlib_cmp(remainder_encoded_len, ==, t2)); + memcpy(buffer + old_len + direct_encoded_len, remainder_buffer, remainder_encoded_len); + + BSON_ASSERT(old_len + direct_encoded_len + remainder_truncated_len == max_len); + buffer[max_len] = '\0'; + string->len = max_len; + mcommon_string_append_overflow(append); + return false; + } +} + +bool +mcommon_string_append_oid_as_hex(mcommon_string_append_t *append, const bson_oid_t *value) +{ + BSON_ASSERT_PARAM(append); + BSON_ASSERT_PARAM(value); + + char oid_str[25]; + bson_oid_to_string(value, oid_str); + return mcommon_string_append(append, oid_str); +} + +bool +mcommon_string_append_selected_chars(mcommon_string_append_t *append, + const char *tmplt, + const char *selector, + size_t selector_len) +{ + BSON_ASSERT_PARAM(append); + BSON_ASSERT_PARAM(tmplt); + BSON_ASSERT_PARAM(selector); + + for (uint8_t template_char; (template_char = (uint8_t)*tmplt); tmplt++) { + BSON_ASSERT(template_char <= 0x7f); + if (memchr(selector, template_char, selector_len) && !mcommon_string_append_unichar(append, template_char)) { + return false; + } + } + return mcommon_string_status_from_append(append); +} + +bool +mcommon_string_append_printf(mcommon_string_append_t *append, const char *format, ...) +{ + BSON_ASSERT_PARAM(append); + BSON_ASSERT_PARAM(format); + + va_list args; + va_start(args, format); + bool ret = mcommon_string_append_vprintf(append, format, args); + va_end(args); + return ret; +} + +bool +mcommon_string_append_vprintf(mcommon_string_append_t *append, const char *format, va_list args) +{ + BSON_ASSERT_PARAM(append); + BSON_ASSERT_PARAM(format); + + if (BSON_UNLIKELY(!mcommon_string_status_from_append(append))) { + return false; + } + + mcommon_string_t *string = append->_string; + uint32_t old_len = string->len; + uint32_t max_len = append->_max_len; + BSON_ASSERT(max_len < UINT32_MAX); + uint32_t max_append_len = old_len < max_len ? max_len - old_len : 0; + + // Initial minimum buffer length; increases on retry. + uint32_t min_format_buffer_capacity = 16; + + while (true) { + // Allocate room for a format buffer at the end of the string. + // It will be at least this round's min_format_buffer_capacity, but if we happen to have extra space allocated we + // do want that to be available to vsnprintf(). + + min_format_buffer_capacity = BSON_MIN(min_format_buffer_capacity, max_append_len); + mcommon_string_grow_to_capacity(string, old_len + min_format_buffer_capacity); + uint32_t alloc = string->alloc; + BSON_ASSERT(alloc > 0 && alloc - 1u >= old_len); + char *format_buffer = string->str + old_len; + uint32_t actual_format_buffer_capacity = BSON_MIN(alloc - 1u - old_len, max_append_len); + BSON_ASSERT(actual_format_buffer_capacity >= min_format_buffer_capacity); + BSON_ASSERT(actual_format_buffer_capacity < UINT32_MAX); + uint32_t format_buffer_alloc = actual_format_buffer_capacity + 1u; + + va_list args_copy; + va_copy(args_copy, args); + int format_result = bson_vsnprintf(format_buffer, format_buffer_alloc, format, args_copy); + va_end(args_copy); + + if (format_result > -1 && mlib_in_range(uint32_t, format_result) && + (uint32_t)format_result <= actual_format_buffer_capacity) { + // Successful result, no truncation. + format_buffer[format_result] = '\0'; + string->len = old_len + (uint32_t)format_result; + BSON_ASSERT(string->len <= append->_max_len); + BSON_ASSERT(append->_max_len_exceeded == false); + return true; + } + + if (actual_format_buffer_capacity == max_append_len) { + // No more space to grow into, this must be the final result. + + if (format_result > -1 && mlib_in_range(uint32_t, format_result) && (uint32_t)format_result < UINT32_MAX) { + // We have truncated output from vsnprintf. Clean it up by removing + // any partial UTF-8 sequences that might be left on the end. + uint32_t truncated_append_len = mcommon_utf8_truncate_len( + format_buffer, BSON_MIN(actual_format_buffer_capacity, (uint32_t)format_result)); + BSON_ASSERT(truncated_append_len <= actual_format_buffer_capacity); + format_buffer[truncated_append_len] = '\0'; + string->len = old_len + truncated_append_len; + append->_max_len_exceeded = true; + return false; + } + + // Error from vsnprintf; This operation fails, but we do not set max_len_exceeded. + return false; + } + + // Choose a larger format_buffer_len and try again. Length will be clamped to max_append_len above. + if (format_result > -1 && mlib_in_range(uint32_t, format_result) && (uint32_t)format_result < UINT32_MAX) { + min_format_buffer_capacity = (uint32_t)format_result + 1u; + } else if (min_format_buffer_capacity < UINT32_MAX / 2) { + min_format_buffer_capacity *= 2; + } else { + min_format_buffer_capacity = UINT32_MAX - 1u; + } + } +} diff --git a/bsonjs/common/common-thread-private.h b/bsonjs/common/common-thread-private.h index 291af21..568be83 100644 --- a/bsonjs/common/common-thread-private.h +++ b/bsonjs/common/common-thread-private.h @@ -1,5 +1,5 @@ /* - * Copyright 2013-present MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,41 +14,41 @@ * limitations under the License. */ -#include "common-prelude.h" -#include "common-config.h" -#include "common-macros-private.h" +#include -#ifndef COMMON_THREAD_PRIVATE_H -#define COMMON_THREAD_PRIVATE_H +#include -#define BSON_INSIDE -#include "bson/bson-compat.h" -#include "bson/bson-config.h" -#include "bson/bson-macros.h" -#undef BSON_INSIDE +#include + +#ifndef MONGO_C_DRIVER_COMMON_THREAD_PRIVATE_H +#define MONGO_C_DRIVER_COMMON_THREAD_PRIVATE_H + +#include +#include +#include BSON_BEGIN_DECLS -#define mcommon_thread_create COMMON_NAME (thread_create) -#define mcommon_thread_join COMMON_NAME (thread_join) +#define mcommon_thread_create COMMON_NAME(thread_create) +#define mcommon_thread_join COMMON_NAME(thread_join) #if defined(BSON_OS_UNIX) #include -#define BSON_ONCE_FUN(n) void n (void) +#define BSON_ONCE_FUN(n) void n(void) #define BSON_ONCE_RETURN return #define BSON_ONCE_INIT PTHREAD_ONCE_INIT -#define bson_once(o, c) \ - do { \ - BSON_ASSERT (pthread_once ((o), (c)) == 0); \ +#define bson_once(o, c) \ + do { \ + BSON_ASSERT(pthread_once((o), (c)) == 0); \ } while (0) #define bson_once_t pthread_once_t #define bson_thread_t pthread_t -#define BSON_THREAD_FUN(_function_name, _arg_name) void *(_function_name) (void *(_arg_name)) -#define BSON_THREAD_FUN_TYPE(_function_name) void *(*(_function_name)) (void *) +#define BSON_THREAD_FUN(_function_name, _arg_name) void *(_function_name)(void *(_arg_name)) +#define BSON_THREAD_FUN_TYPE(_function_name) void *(*(_function_name))(void *) #define BSON_THREAD_RETURN return NULL -/* this macro can be defined as a as a build configuration option +/* this macro can be defined as a build configuration option * with -DENABLE_DEBUG_ASSERTIONS=ON. its purpose is to allow for functions * that require a mutex to be locked on entry to assert that the mutex * is actually locked. @@ -56,64 +56,68 @@ BSON_BEGIN_DECLS #ifndef MONGOC_ENABLE_DEBUG_ASSERTIONS -#define bson_mutex_destroy(m) \ - do { \ - BSON_ASSERT (pthread_mutex_destroy ((m)) == 0); \ +#define bson_mutex_destroy(m) \ + do { \ + BSON_ASSERT(pthread_mutex_destroy((m)) == 0); \ } while (0) -#define bson_mutex_init(_n) \ - do { \ - BSON_ASSERT (pthread_mutex_init ((_n), NULL) == 0); \ +#define bson_mutex_init(_n) \ + do { \ + BSON_ASSERT(pthread_mutex_init((_n), NULL) == 0); \ } while (0) -#define bson_mutex_lock(m) \ - do { \ - BSON_ASSERT (pthread_mutex_lock ((m)) == 0); \ +#define bson_mutex_lock(m) \ + do { \ + BSON_ASSERT(pthread_mutex_lock((m)) == 0); \ } while (0) #define bson_mutex_t pthread_mutex_t -#define bson_mutex_unlock(m) \ - do { \ - BSON_ASSERT (pthread_mutex_unlock ((m)) == 0); \ +#define bson_mutex_unlock(m) \ + do { \ + BSON_ASSERT(pthread_mutex_unlock((m)) == 0); \ } while (0) #else +#include + typedef struct { pthread_t lock_owner; pthread_mutex_t wrapped_mutex; bool valid_tid; } bson_mutex_t; -#define bson_mutex_destroy(mutex) \ - do { \ - BSON_ASSERT (pthread_mutex_destroy (&(mutex)->wrapped_mutex) == 0); \ +#define bson_mutex_destroy(mutex) \ + do { \ + BSON_ASSERT(pthread_mutex_destroy(&(mutex)->wrapped_mutex) == 0); \ } while (0); -#define bson_mutex_init(mutex) \ - do { \ - BSON_ASSERT (pthread_mutex_init (&(mutex)->wrapped_mutex, NULL) == 0); \ - (mutex)->valid_tid = false; \ +#define bson_mutex_init(mutex) \ + do { \ + BSON_ASSERT(pthread_mutex_init(&(mutex)->wrapped_mutex, NULL) == 0); \ + (mutex)->valid_tid = false; \ } while (0); -#define bson_mutex_lock(mutex) \ - do { \ - BSON_ASSERT (pthread_mutex_lock (&(mutex)->wrapped_mutex) == 0); \ - (mutex)->lock_owner = pthread_self (); \ - (mutex)->valid_tid = true; \ +#define bson_mutex_lock(mutex) \ + do { \ + BSON_ASSERT(pthread_mutex_lock(&(mutex)->wrapped_mutex) == 0); \ + (mutex)->lock_owner = pthread_self(); \ + (mutex)->valid_tid = true; \ } while (0); -#define bson_mutex_unlock(mutex) \ - do { \ - (mutex)->valid_tid = false; \ - BSON_ASSERT (pthread_mutex_unlock (&(mutex)->wrapped_mutex) == 0); \ +#define bson_mutex_unlock(mutex) \ + do { \ + (mutex)->valid_tid = false; \ + BSON_ASSERT(pthread_mutex_unlock(&(mutex)->wrapped_mutex) == 0); \ } while (0); #endif #else #include -#define BSON_ONCE_FUN(n) BOOL CALLBACK n (PINIT_ONCE _ignored_a, PVOID _ignored_b, PVOID *_ignored_c) +#define BSON_ONCE_FUN(n) \ + BOOL CALLBACK MLIB_PRAGMA_IF_MSVC(warning(push)) MLIB_PRAGMA_IF_MSVC(warning(disable : 4100)) \ + n(PINIT_ONCE _ignored_a, PVOID _ignored_b, PVOID *_ignored_c) MLIB_PRAGMA_IF_MSVC(warning(pop)) #define BSON_ONCE_INIT INIT_ONCE_STATIC_INIT #define BSON_ONCE_RETURN return true #define bson_mutex_destroy DeleteCriticalSection @@ -121,14 +125,14 @@ typedef struct { #define bson_mutex_lock EnterCriticalSection #define bson_mutex_t CRITICAL_SECTION #define bson_mutex_unlock LeaveCriticalSection -#define bson_once(o, c) \ - do { \ - BSON_ASSERT (InitOnceExecuteOnce ((o), (c), NULL, NULL)); \ +#define bson_once(o, c) \ + do { \ + BSON_ASSERT(InitOnceExecuteOnce((o), (c), NULL, NULL)); \ } while (0) #define bson_once_t INIT_ONCE #define bson_thread_t HANDLE -#define BSON_THREAD_FUN(_function_name, _arg_name) unsigned (__stdcall _function_name) (void *(_arg_name)) -#define BSON_THREAD_FUN_TYPE(_function_name) unsigned (__stdcall * _function_name) (void *) +#define BSON_THREAD_FUN(_function_name, _arg_name) unsigned(__stdcall _function_name)(void *(_arg_name)) +#define BSON_THREAD_FUN_TYPE(_function_name) unsigned(__stdcall * _function_name)(void *) #define BSON_THREAD_RETURN return 0 #endif @@ -136,17 +140,17 @@ typedef struct { * libmongoc or _bson for libbson) to avoid duplicate symbols when linking both * libbson and libmongoc statically. */ int -mcommon_thread_join (bson_thread_t thread); +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); +mcommon_thread_create(bson_thread_t *thread, BSON_THREAD_FUN_TYPE(func), void *arg); #if defined(MONGOC_ENABLE_DEBUG_ASSERTIONS) && defined(BSON_OS_UNIX) -#define mcommon_mutex_is_locked COMMON_NAME (mutex_is_locked) +#define mcommon_mutex_is_locked COMMON_NAME(mutex_is_locked) bool -mcommon_mutex_is_locked (bson_mutex_t *mutex); +mcommon_mutex_is_locked(bson_mutex_t *mutex); #endif /** @@ -158,52 +162,52 @@ mcommon_mutex_is_locked (bson_mutex_t *mutex); * while another thread holds in shared mode, and vice-versa. */ typedef struct bson_shared_mutex_t { - BSON_IF_WINDOWS (SRWLOCK native;) - BSON_IF_POSIX (pthread_rwlock_t native;) + BSON_IF_WINDOWS(SRWLOCK native;) + BSON_IF_POSIX(pthread_rwlock_t native;) } bson_shared_mutex_t; static BSON_INLINE void -bson_shared_mutex_init (bson_shared_mutex_t *mtx) +bson_shared_mutex_init(bson_shared_mutex_t *mtx) { - BSON_IF_WINDOWS (InitializeSRWLock (&mtx->native)); - BSON_IF_POSIX (BSON_ASSERT (pthread_rwlock_init (&mtx->native, NULL) == 0);) + BSON_IF_WINDOWS(InitializeSRWLock(&mtx->native)); + BSON_IF_POSIX(BSON_ASSERT(pthread_rwlock_init(&mtx->native, NULL) == 0);) } static BSON_INLINE void -bson_shared_mutex_destroy (bson_shared_mutex_t *mtx) +bson_shared_mutex_destroy(bson_shared_mutex_t *mtx) { - BSON_IF_WINDOWS ((void) mtx;) - BSON_IF_POSIX (BSON_ASSERT (pthread_rwlock_destroy (&mtx->native) == 0);) + BSON_IF_WINDOWS((void)mtx;) + BSON_IF_POSIX(BSON_ASSERT(pthread_rwlock_destroy(&mtx->native) == 0);) } static BSON_INLINE void -bson_shared_mutex_lock_shared (bson_shared_mutex_t *mtx) +bson_shared_mutex_lock_shared(bson_shared_mutex_t *mtx) { - BSON_IF_WINDOWS (AcquireSRWLockShared (&mtx->native);) - BSON_IF_POSIX (BSON_ASSERT (pthread_rwlock_rdlock (&mtx->native) == 0);) + BSON_IF_WINDOWS(AcquireSRWLockShared(&mtx->native);) + BSON_IF_POSIX(BSON_ASSERT(pthread_rwlock_rdlock(&mtx->native) == 0);) } static BSON_INLINE void -bson_shared_mutex_lock (bson_shared_mutex_t *mtx) +bson_shared_mutex_lock(bson_shared_mutex_t *mtx) { - BSON_IF_WINDOWS (AcquireSRWLockExclusive (&mtx->native);) - BSON_IF_POSIX (BSON_ASSERT (pthread_rwlock_wrlock (&mtx->native) == 0);) + BSON_IF_WINDOWS(AcquireSRWLockExclusive(&mtx->native);) + BSON_IF_POSIX(BSON_ASSERT(pthread_rwlock_wrlock(&mtx->native) == 0);) } static BSON_INLINE void -bson_shared_mutex_unlock (bson_shared_mutex_t *mtx) +bson_shared_mutex_unlock(bson_shared_mutex_t *mtx) { - BSON_IF_WINDOWS (ReleaseSRWLockExclusive (&mtx->native);) - BSON_IF_POSIX (BSON_ASSERT (pthread_rwlock_unlock (&mtx->native) == 0);) + BSON_IF_WINDOWS(ReleaseSRWLockExclusive(&mtx->native);) + BSON_IF_POSIX(BSON_ASSERT(pthread_rwlock_unlock(&mtx->native) == 0);) } static BSON_INLINE void -bson_shared_mutex_unlock_shared (bson_shared_mutex_t *mtx) +bson_shared_mutex_unlock_shared(bson_shared_mutex_t *mtx) { - BSON_IF_WINDOWS (ReleaseSRWLockShared (&mtx->native);) - BSON_IF_POSIX (BSON_ASSERT (pthread_rwlock_unlock (&mtx->native) == 0);) + BSON_IF_WINDOWS(ReleaseSRWLockShared(&mtx->native);) + BSON_IF_POSIX(BSON_ASSERT(pthread_rwlock_unlock(&mtx->native) == 0);) } BSON_END_DECLS -#endif /* COMMON_THREAD_PRIVATE_H */ +#endif /* MONGO_C_DRIVER_COMMON_THREAD_PRIVATE_H */ diff --git a/bsonjs/common/common-thread.c b/bsonjs/common/common-thread.c index 9b26f6e..9f3e07d 100644 --- a/bsonjs/common/common-thread.c +++ b/bsonjs/common/common-thread.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-present MongoDB, Inc. + * Copyright 2009-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,59 +14,59 @@ * limitations under the License. */ -#include "common-thread-private.h" +#include #include #if defined(BSON_OS_UNIX) int -mcommon_thread_create (bson_thread_t *thread, BSON_THREAD_FUN_TYPE (func), void *arg) +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); + BSON_ASSERT_PARAM(thread); + BSON_ASSERT_PARAM(func); + BSON_OPTIONAL_PARAM(arg); // optional. + return pthread_create(thread, NULL, func, arg); } int -mcommon_thread_join (bson_thread_t thread) +mcommon_thread_join(bson_thread_t thread) { - return pthread_join (thread, NULL); + return pthread_join(thread, NULL); } #if defined(MONGOC_ENABLE_DEBUG_ASSERTIONS) && defined(BSON_OS_UNIX) bool -mcommon_mutex_is_locked (bson_mutex_t *mutex) +mcommon_mutex_is_locked(bson_mutex_t *mutex) { - return mutex->valid_tid && pthread_equal (pthread_self (), mutex->lock_owner); + return mutex->valid_tid && pthread_equal(pthread_self(), mutex->lock_owner); } #endif #else int -mcommon_thread_create (bson_thread_t *thread, BSON_THREAD_FUN_TYPE (func), void *arg) +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. + BSON_ASSERT_PARAM(thread); + BSON_ASSERT_PARAM(func); + BSON_OPTIONAL_PARAM(arg); // optional. - *thread = (HANDLE) _beginthreadex (NULL, 0, func, arg, 0, NULL); + *thread = (HANDLE)_beginthreadex(NULL, 0, func, arg, 0, NULL); if (0 == *thread) { return errno; } return 0; } int -mcommon_thread_join (bson_thread_t thread) +mcommon_thread_join(bson_thread_t thread) { int ret; /* zero indicates success for WaitForSingleObject. */ - ret = WaitForSingleObject (thread, INFINITE); + ret = WaitForSingleObject(thread, INFINITE); if (WAIT_OBJECT_0 != ret) { return ret; } /* zero indicates failure for CloseHandle. */ - ret = CloseHandle (thread); + ret = CloseHandle(thread); if (0 == ret) { return 1; } diff --git a/bsonjs/common/common-utf8-private.h b/bsonjs/common/common-utf8-private.h new file mode 100644 index 0000000..890e8d2 --- /dev/null +++ b/bsonjs/common/common-utf8-private.h @@ -0,0 +1,182 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef MONGO_C_DRIVER_COMMON_UTF8_PRIVATE_H +#define MONGO_C_DRIVER_COMMON_UTF8_PRIVATE_H + +#include + + +/* + *-------------------------------------------------------------------------- + * + * mcommon_utf8_get_sequence -- + * + * Determine the sequence length of the first UTF-8 character in + * @utf8. The sequence length is stored in @seq_length and the mask + * for the first character is stored in @first_mask. + * + * Returns: + * None. + * + * Side effects: + * @seq_length is set. + * @first_mask is set. + * + *-------------------------------------------------------------------------- + */ + +static BSON_INLINE void +mcommon_utf8_get_sequence(const char *utf8, /* IN */ + uint8_t *seq_length, /* OUT */ + uint8_t *first_mask) /* OUT */ +{ + unsigned char c = *(const unsigned char *)utf8; + uint8_t m; + uint8_t n; + + /* + * See the following[1] for a description of what the given multi-byte + * sequences will be based on the bits set of the first byte. We also need + * to mask the first byte based on that. All subsequent bytes are masked + * against 0x3F. + * + * [1] http://www.joelonsoftware.com/articles/Unicode.html + */ + + if ((c & 0x80) == 0) { + n = 1; + m = 0x7F; + } else if ((c & 0xE0) == 0xC0) { + n = 2; + m = 0x1F; + } else if ((c & 0xF0) == 0xE0) { + n = 3; + m = 0x0F; + } else if ((c & 0xF8) == 0xF0) { + n = 4; + m = 0x07; + } else { + n = 0; + m = 0; + } + + *seq_length = n; + *first_mask = m; +} + + +/* + *-------------------------------------------------------------------------- + * + * mcommon_utf8_from_unichar -- + * + * Converts the unichar to a sequence of utf8 bytes and stores those + * in @utf8. The number of bytes in the sequence are stored in @len. + * + * Parameters: + * @unichar: A bson_unichar_t. + * @utf8: A location for the multi-byte sequence. + * @len: A location for number of bytes stored in @utf8. + * + * Returns: + * None. + * + * Side effects: + * @utf8 is set. + * @len is set. + * + *-------------------------------------------------------------------------- + */ + +static BSON_INLINE void +mcommon_utf8_from_unichar(bson_unichar_t unichar, /* IN */ + char utf8[BSON_ENSURE_ARRAY_PARAM_SIZE(6)], /* OUT */ + uint32_t *len) /* OUT */ +{ + BSON_ASSERT_PARAM(len); + + if (unichar <= 0x7F) { + utf8[0] = unichar; + *len = 1; + } else if (unichar <= 0x7FF) { + *len = 2; + utf8[0] = 0xC0 | ((unichar >> 6) & 0x3F); + utf8[1] = 0x80 | ((unichar) & 0x3F); + } else if (unichar <= 0xFFFF) { + *len = 3; + utf8[0] = 0xE0 | ((unichar >> 12) & 0xF); + utf8[1] = 0x80 | ((unichar >> 6) & 0x3F); + utf8[2] = 0x80 | ((unichar) & 0x3F); + } else if (unichar <= 0x1FFFFF) { + *len = 4; + utf8[0] = 0xF0 | ((unichar >> 18) & 0x7); + utf8[1] = 0x80 | ((unichar >> 12) & 0x3F); + utf8[2] = 0x80 | ((unichar >> 6) & 0x3F); + utf8[3] = 0x80 | ((unichar) & 0x3F); + } else { + *len = 0; + } +} + + +/* + * @brief Calculate a truncation length that preserves UTF-8 validity + * @param str String data, at least 'len' bytes long. + * @returns A new length <= 'len' + * + * When 'str' is a valid UTF-8 string with length >= 'len' bytes, + * this calculates a new length, less than or equal to 'len', which + * guarantees that the string will be truncated in-between code points. + */ + +static BSON_INLINE uint32_t +mcommon_utf8_truncate_len(const char *str, uint32_t len) +{ + uint32_t resulting_len = len; + while (resulting_len > 0) { + if (BSON_LIKELY((uint8_t)str[resulting_len - 1u] <= 0x7f)) { + // Single-byte sequence, always a fine place to stop + return resulting_len; + } + + // Search for the last byte that could begin a UTF-8 sequence + uint32_t seq_begin_at = resulting_len - 1u; + while (((uint8_t)str[seq_begin_at] & 0xc0) == 0x80) { + if (seq_begin_at > 0) { + seq_begin_at--; + } else { + return 0; + } + } + + uint8_t seq_length, first_mask_unused; + mcommon_utf8_get_sequence(str + seq_begin_at, &seq_length, &first_mask_unused); + if (seq_begin_at + seq_length == resulting_len) { + // Sequence is complete, we can truncate here. + return resulting_len; + } + + // Sequence was truncated or invalid; resume search prior to it's beginning. + resulting_len = seq_begin_at; + } + return 0; +} + + +#endif /* MONGO_C_DRIVER_COMMON_UTF8_PRIVATE_H */ diff --git a/bsonjs/common/mlib/ckdint.h b/bsonjs/common/mlib/ckdint.h new file mode 100644 index 0000000..4e05071 --- /dev/null +++ b/bsonjs/common/mlib/ckdint.h @@ -0,0 +1,679 @@ +/** + * @file mlib/ckdint.h + * @brief Checked integer arithmetic + * @date 2025-02-04 + * + * This file implements the C23 checked-integer-arithmetic functions as macros. + * + * The function-like macros are defined: + * + * - `mlib_add(Dst, L, R)` / `mlib_add(Dst, A)` + * - `mlib_sub(Dst, L, R)` / `mlib_sub(Dst, A)` + * - `mlib_mul(Dst, L, R)` / `mlib_mul(Dst, A)` + * - `mlib_narrow(Dst, V)` (not from stdckdint, but defined as `mlib_add(Dst, V, 0)`) + * + * Where `Dst` is a pointer to integral storage, and `L` and `R` are arbitrary + * integral expressions. The two-argument variants treat `Dst` as the left-hand + * operand for in-place arithmetic. + * + * Each macro accepts arguments of arbitrary type at any position, and will "do + * the right thing", regardless of the parameter types. No funny integer promotion, + * sign extension, sign conversion, nor implicit narrowing. The macros return `false` + * if-and-only-if the result was lossless. They return `true` if-and-only-if the + * value written to `Dst` does not represent the true arithmetic result. + * + * The following additional macros are defined: + * + * - `mlib_assert_add(T, L, R)` + * - `mlib_assert_sub(T, L, R)` + * - `mlib_assert_mul(T, L, R)` + * + * Where `T` is an integer type. The macro will yield a value of that type, asserting + * that the operation on `L` and `R` does not overflow. If the operation overflows, + * the program will be terminated with a diagnostic to `stderr` pointing to the call site. + * + * For implementation details and a usage guide, see `ckdint.md` + * + * @copyright Copyright (c) 2025 + * + * 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. + */ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +mlib_extern_c_begin(); + +/** + * @brief Perform integer addition + * + * @param Out A non-null pointer to a modifiable integer. + * @param A The left-hand addend of addition + * @param B The right-hand addend of addition + * @retval `true` if the value written to `Out` does not represent the true arithmetic sum. + * @retval `false` Otherwise. + * + * The sum `A + B` is written to `Out`. The operation is commutative. + * + * If the argument `B` is omitted, computes `*Out + A` (performs in-place addition). + */ +#define mlib_add(...) MLIB_ARGC_PICK(_mlib_ckdint, mlib_add, __VA_ARGS__) +/** + * @brief Perform integer subtraction + * + * @param Out A non-null pointer to a modifiable integer. + * @param A The left-hand operand of the subtraction (minuend) + * @param B The right-hand operand of subtraction (subtrahend) + * @retval `true` if the value written to `Out` does not represent the true arithmetic difference. + * @retval `false` Otherwise. + * + * The difference `A - B` will be written to `Out`. + * + * If the argument `B` is omitted, computes `*Out - A` (performs in-place subtraction) + */ +#define mlib_sub(...) MLIB_ARGC_PICK(_mlib_ckdint, mlib_sub, __VA_ARGS__) +/** + * @brief Perform integer multiplication + * + * @param Out A non-null pointer to a modifiable integer. + * @param A The left-hand factor of multiplication + * @param B The right-hand factor of multiplication + * @retval `true` if the value written to `Out` does not represent the true arithmetic product. + * @retval `false` Otherwise. + * + * The product `A × B` will be written to `Out`. The operation is commutative. + * + * If the argument `B` is omitted, computes `Out × A` (performs in-place multiplication) + */ +#define mlib_mul(...) MLIB_ARGC_PICK(_mlib_ckdint, mlib_mul, __VA_ARGS__) +/** + * @brief Perform narrowing assignment from one integer value to another. + * + * @param Out A non-null pointer to a modifiable integer. + * @param A The integer value to be narrowed. + * @retval `true` if the value written to `Out` is not equivalent to the value of `A` + * @retval `false` otherwise + */ +#define mlib_narrow(O, A) mlib_add((O), (A), 0) + +/** + * @brief Perform an asserting addition, yielding the result + * + * @param T The target type of the operation + * @param A The left-hand addend for the addition + * @param B The right-hand addend for the addition + * @return The sum `A + B` as type `T` + * + * If the true arithmetic sum is not representable in `T`, the program terminates. + */ +#define mlib_assert_add(T, A, B) \ + _mlib_assert_ckdint(T, A, B, &mlib_add, "mlib_assert_add", #T, #A, #B, mlib_this_source_location()) +/** + * @brief Perform an asserting subtraction, yielding the result + * + * @param T The target type of the operation + * @param A The left-hand minuend for the subtraction + * @param B The right-hand subtrahend for the subtraction + * @return The difference `A - B` as type `T` + * + * If the true arithmetic difference is not representable in `T`, the program terminates. + */ +#define mlib_assert_sub(T, A, B) \ + _mlib_assert_ckdint(T, A, B, &mlib_sub, "mlib_assert_sub", #T, #A, #B, mlib_this_source_location()) +/** + * @brief Perform an asserting multiplication, yielding the result + * + * @param T The target type of the operation + * @param A The left-hand factor for the multiplication + * @param B The right-hand factor for the multiplication + * @return The product `A × B` as type `T` + * + * If the true arithmetic product is not representable in `T`, the program terminates. + */ +#define mlib_assert_mul(T, A, B) \ + _mlib_assert_ckdint(T, A, B, &mlib_mul, "mlib_assert_mul", #T, #A, #B, mlib_this_source_location()) + +/** + * @brief Perform a runtime-checked cast of an integral value to another type. + * + * @param T A type specifier for a target integral type for the cast. + * @param Operand The integral value to be converted. + * + * If the cast would result in the operand value changing, the program will be + * terminated with a diagnostic. + */ +#define mlib_assert_narrow(T, Operand) \ + (T) _mlib_checked_cast( \ + mlib_minof(T), mlib_maxof(T), mlib_upsize_integer(Operand), #T, #Operand, mlib_this_source_location()) + +#define _mlib_ckdint_argc_3(Fn, Out, Arg) _mlib_ckdint_argc_4(Fn, Out, *(Out), Arg) +#define _mlib_ckdint_argc_4(Fn, O, A, B) \ + _mlib_ckdint(O, \ + sizeof(*(O)), \ + _mlibClobberIsSigned(*(O), 0) ? (intmax_t)_mlibMinofSigned(*(O)) : _mlibMinofUnsigned(*(O)), \ + _mlibClobberIsSigned(*(O), 1) ? _mlibMaxofSigned(*(O)) : _mlibMaxofUnsigned(*(O)), \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + &Fn) + +// Impl macro for the asserting checked arithmetic functions +#define _mlib_assert_ckdint(T, A, B, Fn, F_str, T_str, A_str, B_str, Here) \ + ((T)_mlib_assert_ckdint(sizeof(T), \ + mlib_minof(T), \ + mlib_maxof(T), \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + Fn, \ + F_str, \ + T_str, \ + A_str, \ + B_str, \ + Here)) + +// clang-format off +// Generates an 0b11111 bit pattern for appropriate size: +#define _mlibMaxofUnsigned(V) \ + /* NOLINTNEXTLINE(bugprone-sizeof-expression) */ \ + mlib_bits(mlib_bitsizeof((V)), 0) + +// Generates an 0b01111 bit pattern for the two's complement max value: +#define _mlibMaxofSigned(V) \ + /* NOLINTNEXTLINE(bugprone-sizeof-expression) */ \ + mlib_bits(mlib_bitsizeof(V) - 1u, 0) +// Generates an 0b10000... bit pattern for the two's complement min value: +#define _mlibMinofSigned(V) \ + /* NOLINTNEXTLINE(bugprone-sizeof-expression) */ \ + (0 - mlib_bits(1, mlib_bitsizeof(V) - 1u)) +// For completeness: +#define _mlibMinofUnsigned(V) 0 +// Yields true iff the operand expression has a signed type, but requires that +// the operand is a modifiable l-value. The `N` must be 0 or 1, arbitrarily (see below). +#define _mlibClobberIsSigned(V, N) \ + MLIB_IF_ELSE(mlib_have_typeof()) \ + /* Prefer using typeof(), if we can. */ \ + (mlib_is_signed(mlib_typeof(V))) \ + /* Otherwise, do a dance: */ \ + (( \ + /* Save the value of V */ \ + _mlibSignCheckTmp[N] = 0ull | (uintmax_t) (V), \ + /* Set V to zero, and check whether decrementing results in a negative value */ \ + (V) = 0, \ + _mlibSignCheckResult[N] = (--(V) < 0), \ + /* Restore the value of V (bit hacks to prevent conversion warnings) */ \ + (V) = 0, \ + (V) |= _mlibSignCheckTmp[N], \ + /* Yield the sign-check result */ \ + _mlibSignCheckResult[N] \ + )) +// Storage for `_mlibClobberIsSigned`. We use more than one storage space to prevent +// unsequenced-operation warnings when we use `_mlibClobberIsSigned` multiple times +// in a function argument list. GCC and Clang are able to easily elide these from +// an optimized TU. MSVC has trouble, but is still able to constant-fold where it matters. +static mlib_maybe_unused mlib_thread_local uintmax_t _mlibSignCheckTmp[2]; +static mlib_maybe_unused mlib_thread_local bool _mlibSignCheckResult[2]; +// clang-format on + +// Compile-time assert that the compiler's integer conversions obey two's complement encoding +mlib_static_assert((intmax_t)UINTMAX_MAX == -1 // + && (intmax_t)(UINTMAX_MAX - 5) == -6, + "This file requires two's complement signed integers"); + +/** + * @brief Function signature for checked arithmetic support functions + * + * The function operates on max-precision integers of either sign, and should + * return true iff the arithmetic operation overflows for the given sign configuration. + * + * @param dst The output parameter for the operation. Never a null pointer. + * @param dst_signed Whether to treat the result as a signed integer + * @param a_signed Whether to treat the `a` operand as signed + * @param a The left-hand operand of the operation + * @param b_signed Whether to treat the `b` operand as signed + * @param b The right-hand operand of the operation + * + * @note This was original written to use `mlib_upscaled_integer` for `a/b/dst`, but + * this defeats MSVC's ability to inline an indirect call through a constant-folded function + * pointer with this signature. GCC and Clang handle this fine, but for MSVC performance + * the more verbose signature is used. + */ +typedef bool (*_mlib_ckdint_arith_fn)( + uintmax_t *dst, bool dst_signed, bool a_signed, uintmax_t a, bool b_signed, uintmax_t b); + +// Support function for the `mlib_add` macro +static inline bool(mlib_add)(uintmax_t *dst, bool dst_signed, bool a_signed, uintmax_t a, bool b_signed, uintmax_t b) + mlib_noexcept +{ + // Perform regular wrapping arithmetic on the unsigned value. The bit pattern + // is equivalent if there is two's complement signed arithmetic. + const uintmax_t sum = *dst = a + b; + const uintmax_t signbit = mlib_bits(1, mlib_bitsizeof(uintmax_t) - 1u); + // Now we check whether that overflowed according to the sign configuration. + // We use some bit fiddling magic that treat the signbit as a boolean for + // "is this number negative?" or "is this number “large” (i.e. bigger than signed-max)?" + // The expanded verbose form of each bit-magic is written below the more esoteric cases + if (dst_signed) { + if (a_signed) { + if (b_signed) { // S = S + S + return signbit & (sum ^ a) & (sum ^ b); + // Expanded: + // Test whether the product sign is unequal to both input signs + // X ^ Y yields a negative value if the signs are unequal + // const bool a_signflipped = (intmax_t) (sum ^ a) < 0; + // const bool b_signflipped = (intmax_t) (sum ^ b) < 0; + // return a_signflipped && b_signflipped; + } else { // S = S + U + // Flip the sign bit of a, test whether that sum overflows + a ^= signbit; + return a + b < a; + } + } else { + if (b_signed) { // S = U + S + // Flip the sign bit of `b`, test whether that sum overflows + b ^= signbit; + return a + b < b; + } else { // S = U + U + // The signed sum must not be less than the addend, and not negative + return sum < a || (signbit & sum); + } + } + } else { + if (a_signed) { + if (b_signed) { // U = S + S + return signbit & (((sum | a) & b) | ((sum & a) & ~b)); + // Expanded: + // const bool a_is_negative = (intmax_t) a < 0; + // const bool b_is_negative = (intmax_t) b < 0; + // const bool sum_is_large = sum > INTMAX_MAX; + // if (b_is_negative) { + // if (a_is_negative) { + // // The sum must be negative, and therefore cannot be stored in an unsigned + // return true; + // } else if (sum_is_large) { + // // We added a negative value B to a positive value A, but the sum + // // ended up larger than the max signed value, so we wrapped + // return true; + // } + // } else if (a_is_negative) { + // if (sum_is_large) { + // // Same as above case with sum_is_large + // return true; + // } + // } + // return false; + } else { // U = S + U + return signbit & (sum ^ a ^ signbit) & (sum ^ b); + // Expanded: + // const bool sum_is_large = sum > INTMAX_MAX; + // const bool b_is_large = b > INTMAX_MAX; + // const bool a_is_negative = (intmax_t) a < 0; + // if (!a_is_negative && b_is_large) { + // // We are adding a non-negative value to a large number, so the + // // sum must also be large + // if (!sum_is_large) { + // // We ended up with a smaller value, meaning that we must have wrapped + // return true; + // } + // } + // if (a_is_negative && !b_is_large) { + // // We subtracted a non-negative value from a non-large number, so + // // the result should not be large + // if (sum_is_large) { + // // We ended up with a large value, so we must have wrapped + // return true; + // } + // } + // return false; + } + } else { + if (b_signed) { // U = U + S --- (See [U = S + U] for an explanation) + return signbit & (sum ^ a) & (sum ^ b ^ signbit); + } else { // U = U + U (simple case) + return sum < a; + } + } + } +} + +// Support for the `mlib_sub` macro +static inline bool(mlib_sub)(uintmax_t *dst, bool dst_signed, bool a_signed, uintmax_t a, bool b_signed, uintmax_t b) + mlib_noexcept +{ + // Perform the subtraction using regular wrapping arithmetic + const uintmax_t diff = *dst = a - b; + const uintmax_t signbit = mlib_bits(1, mlib_bitsizeof(uintmax_t) - 1u); + // Test whether the operation overflowed for the given sign configuration + // (See mlib_add for more details on why we do this bit fiddling) + if (dst_signed) { + const bool diff_is_negative = signbit & diff; + if (a_signed) { + if (b_signed) { // S = S - S + return signbit & (a ^ b) & (diff ^ a); + // Explain: + // const bool a_is_negative = (intmax_t) a < 0; + // const bool b_is_negative = (intmax_t) b < 0; + // if (a_is_negative != b_is_negative) { + // // Given: Pos - Neg = Pos + // // ∧ Neg - Pos = Neg + // // We expect that the difference preserves the sign of the minuend + // if (diff_is_negative != a_is_negative) { + // return true; + // } + // } + // // Otherwise, `Pos - Pos` and `Neg - Neg` cannot possibly overflow + // return false; + } else { // S = S - U + // The diff overflows if the sign-bit-flipped minuend is smaller than the subtrahend + return (a ^ signbit) < b; + } + } else { + if (b_signed) { // S = U - S + // The diff overflows if the sign-bit-flipped subtrahend is greater than or equal to the minuend + return a >= (b ^ signbit); + } else { // S = U - U + const bool expect_negative = a < b; + return expect_negative != diff_is_negative; + } + } + } else { + if (a_signed) { + if (b_signed) { // U = S - S + return signbit & (((diff & a) & b) | ((diff | a) & ~b)); + // Expanded: + // const bool a_is_negative = (intmax_t) a < 0; + // const bool b_is_negative = (intmax_t) b < 0; + // const bool diff_is_large = diff > INTMAX_MAX; + // if (!b_is_negative) { + // if (a_is_negative) { + // // We subtracted a non-negative from a negative value, so the difference + // // must be negative and cannot be stored as unsigned + // return true; + // } + // if (diff_is_large) { + // // We subtracted a positive value from a signed value, so we must not + // // end up with a large value + // return true; + // } + // } + // if (a_is_negative) { + // if (diff_is_large) { + // // A is negative, and there is no possible value that we can subtract + // // from it to obtain this large integer, so we must have overflowed + // return true; + // } + // } + // return false; + } else { // + return (b > a) || (signbit & a); + } + } else { + if (b_signed) { // U = U - S + return signbit & (a ^ b ^ signbit) & (diff ^ a); + // Explain: + // const bool a_is_large = a > INTMAX_MAX; + // const bool b_is_negative = (intmax_t) b < 0; + // const bool diff_is_large = diff > INTMAX_MAX; + // if (a_is_large && b_is_negative) { + // // The difference between a large value and a negative + // // value must also be a large value + // if (!diff_is_large) { + // // We expected another large value to appear. + // return true; + // } + // } + // if (!a_is_large && !b_is_negative) { + // // The difference between a non-large positive value and a non-negative value + // // must not be a large value + // if (diff_is_large) { + // // We did not expect a large difference + // return true; + // } + // } + // return false; + } else { + return a < b; + } + } + } +} + +// Support for the `mlib_mul` macro +static inline bool(mlib_mul)(uintmax_t *dst, bool dst_signed, bool a_signed, uintmax_t a, bool b_signed, uintmax_t b) + mlib_noexcept +{ + // Multiplication is a lot more subtle + const uintmax_t signbit = mlib_bits(1, mlib_bitsizeof(uintmax_t) - 1u); + if (dst_signed) { + if (a_signed) { + if (b_signed) { + // S = S × S + *dst = a * b; + if (((intmax_t)b == -1 && (intmax_t)a == INTMAX_MIN) || ((intmax_t)a == -1 && (intmax_t)b == INTMAX_MIN)) { + // MIN × -1 is undefined + return true; + } + if (a && (intmax_t)*dst / (intmax_t)a != (intmax_t)b) { + // Mult did not preserve the arithmetic identity + return true; + } + return false; + } else { + // S = S × U + *dst = a * b; + const bool a_is_negative = signbit & a; + const uintmax_t positive_a = a_is_negative ? (0 - a) : a; + const uintmax_t positive_prod = positive_a * b; + const bool did_overflow = positive_a && positive_prod / positive_a != b; + if (did_overflow) { + return true; + } + if (positive_prod > (uintmax_t)INTMAX_MAX + (unsigned)a_is_negative) { + return true; + } + return false; + } + } else { + if (b_signed) { + // S = U × S + // Swap args: [S = S × U] + return (mlib_mul)(dst, dst_signed, b_signed, b, a_signed, a); + } else { + // S = U × U + *dst = a * b; + const bool did_overflow = a && *dst / a != b; + if (did_overflow) { + return true; + } + if (signbit & *dst) { + // A negative product indicates wrapping + return true; + } + return false; + } + } + } else { + if (a_signed) { + if (b_signed) { + // U = S × S + // Is either operand the min? + bool either_min = false; + if (signbit & a & b) { + // Both negative: Flip the signs + a = 0 - a; + b = 0 - b; + // MIN is pathological: 0 - MIN = MIN, so we need to check that: + either_min = (intmax_t)a == INTMAX_MIN || (intmax_t)b == INTMAX_MIN; + } + // Check if the product would be a negative number + const bool neg_prod = (signbit & (a ^ b)) && a && b && !either_min; + *dst = a * b; + return neg_prod || (a && *dst / a != b); + } else { + // U = S × U + *dst = a * b; + const bool did_ovr = a && *dst / a != b; + const bool a_is_negative = signbit & a; + if (did_ovr || (a_is_negative && b)) { + return true; + } + return false; + } + } else { + if (b_signed) { + // U = U × S + // Swap to [U = S × U] + return (mlib_mul)(dst, dst_signed, b_signed, b, a_signed, a); + } else { + // U = U × U: Simple: + *dst = a * b; + return a && *dst / a != b; + } + } + } +} + +/** + * @private + * @brief This function performs the narrowing checks around a ckdint funciton + * + * @param dst Pointer to the target interger + * @param dst_sz The size of the target integer, in bytes + * @param minval The minimum value for the result. If negative, the target is treated as signed + * @param maxval The maximum value for the result + * @param a The left-hand operand for the operation + * @param b The right-hand operand for the operation + * @param fn The arithmetic function that performs arithmetic on the max-precision integer + * @return true If the resulting value DOES NOT equal the true arithmetic result + * @return false If the resulting value represents the true arithmetic results + */ +static inline bool +_mlib_ckdint(void *dst, + int dst_sz, + intmax_t minval, + uintmax_t maxval, + struct mlib_upsized_integer a, + struct mlib_upsized_integer b, + _mlib_ckdint_arith_fn fn) mlib_noexcept +{ + // Perform the arithmetic on uintmax_t, for wrapping behavior + uintmax_t tmp; + bool ovr = fn(&tmp, minval < 0, a.is_signed, a.bits.as_unsigned, b.is_signed, b.bits.as_unsigned); + // Endian-adjusting for writing the result + const char *copy_from = (const char *)&tmp; + if (!mlib_is_little_endian()) { + // We need to adjust the copy src in order to truncate the integer for big-endian encoding. + // Number of high bytes that we need to drop: + const int n_drop = (int)sizeof(tmp) - dst_sz; + // Adjust the copy pointer to so that we copy from the most significant byte that + // we wish to keep + copy_from += n_drop; + } else { + // For little-endian native, we don't need to adjust the bytes, since we can just + // truncate using the memcpy() + } + // Send the result to the destination + memcpy(dst, copy_from, (size_t)dst_sz); + // Final range check: + if (minval < 0) { + // Treat the target as signed: + intmax_t idst = (intmax_t)tmp; + return ovr || idst < minval || (idst > 0 && (uintmax_t)idst > maxval); + } else { + return ovr || tmp > maxval; + } +} + +/** + * @internal + * @brief Implementation function for the asserting arithmetic functions + */ +static inline uintmax_t(_mlib_assert_ckdint)(size_t dst_sz, + intmax_t minval, + uintmax_t maxval, + struct mlib_upsized_integer a, + struct mlib_upsized_integer b, + _mlib_ckdint_arith_fn arith, + const char *fn_str, + const char *type_str, + const char *a_str, + const char *b_str, + struct mlib_source_location here) mlib_noexcept +{ + uintmax_t tmp; + bool did_overflow = _mlib_ckdint(&tmp, dst_sz, minval, maxval, a, b, arith); + if (did_overflow) { + fprintf(stderr, + "%s:%d: [in %s]: Call of %s(%s, %s, %s) resulted in arithmetic overflow\n", + here.file, + here.lineno, + here.func, + fn_str, + type_str, + a_str, + b_str); + abort(); + } + if (!mlib_is_little_endian()) { + // We unconditionally set the leading bytes of `tmp`, but big-endian expects + // the lower place values to be in the later bytes. If the target int is + // smaller than intmax, we must shift all the bits over to their proper + // position. This expression is trivially constant-folded by an optimizer. + tmp >>= ((size_t)CHAR_BIT * ((sizeof tmp) - dst_sz)); + } + return tmp; +} + +static inline uintmax_t +_mlib_checked_cast(intmax_t min_, + uintmax_t max_, + struct mlib_upsized_integer val, + const char *typename_, + const char *expr, + struct mlib_source_location here) mlib_noexcept +{ + if (!(mlib_in_range)(min_, max_, val)) { + if (val.is_signed) { + fprintf(stderr, + "%s:%d: in [%s]: Checked integer cast of “%s” (value = %lld) to “%s” loses information\n", + here.file, + here.lineno, + here.func, + expr, + (long long)val.bits.as_signed, + typename_); + } else { + fprintf(stderr, + "%s:%d: in [%s]: Checked integer cast of “%s” (value = %llu) to “%s” loses information\n", + here.file, + here.lineno, + here.func, + expr, + (unsigned long long)val.bits.as_unsigned, + typename_); + } + fflush(stderr); + abort(); + } + if (val.is_signed) { + return (uintmax_t)val.bits.as_signed; + } + return val.bits.as_unsigned; +} + +mlib_extern_c_end(); diff --git a/bsonjs/common/mlib/cmp.h b/bsonjs/common/mlib/cmp.h new file mode 100644 index 0000000..8a674ff --- /dev/null +++ b/bsonjs/common/mlib/cmp.h @@ -0,0 +1,132 @@ +/** + * @file mlib/cmp.h + * @brief Safe integer comparison and range checking + * @date 2024-08-29 + * + * This file provides safe and intuitive integer comparison macros that behave + * appropriately, regardless of the sign or precision of the integer operands. + * + * @copyright Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MLIB_CMP_H_INCLUDED +#define MLIB_CMP_H_INCLUDED + +#include +#include + +#include +#include + +/** + * @brief Result type of comparing two integral values with `mlib_cmp` + * + * The enumerator values are chosen such that they can be compared with zero + */ +enum mlib_cmp_result { + // The two values are equivalent + mlib_equal = 0, + // The left-hand operand is less than the right-hand + mlib_less = -1, + // The left-hand operand is greater than the right-hand + mlib_greater = 1, +}; + +/** + * @brief Compare two integral values safely. + * + * This function can be called with two arguments or with three: + * + * - `mlib_cmp(a, b)` Returns a value of type `mlib_cmp_result` + * - `mlib_cmp(a, Op, b)` where `Op` is a relational operator. Evaluates to a boolean value. + */ +#define mlib_cmp(...) MLIB_ARGC_PICK(_mlib_cmp, __VA_ARGS__) +// Compare two integers, and return the result of that comparison: +#define _mlib_cmp_argc_2(L, R) mlib_cmp(mlib_upsize_integer((L)), mlib_upsize_integer((R)), 0) +// Compare two integers, but with an infix operator: +#define _mlib_cmp_argc_3(L, Op, R) (mlib_cmp(mlib_upsize_integer((L)), mlib_upsize_integer((R)), 0) Op 0) +// Impl for mlib_cmp +mlib_always_inline static enum mlib_cmp_result(mlib_cmp)(struct mlib_upsized_integer x, + struct mlib_upsized_integer y, + int always_zero) mlib_noexcept +{ + (void)always_zero; +#if mlib_is_optimized_build() && !mlib_is_msvc() && \ + !(defined(MLIB_DISABLE_INLINING_ASSERTIONS) && MLIB_DISABLE_INLINING_ASSERTIONS) + if (always_zero != 0) { + // All calls must pass zero for `always_zero`. In optimized builds, we assert that this function + // is always inlined, and that dead-code-elim triggers to delete this branch that calls + // a never-defined function. + extern void __assert_that_mlib_cmp_was_inlined(void); + // If you see a call to this function ↑ produce a link error here ↓, it means + // that inlining or DCE failed within the relevant translation unit. + __assert_that_mlib_cmp_was_inlined(); + } +#endif + if (x.is_signed) { + if (y.is_signed) { + // Both signed + if (x.bits.as_signed < y.bits.as_signed) { + return mlib_less; + } else if (x.bits.as_signed > y.bits.as_signed) { + return mlib_greater; + } + } else { + // X signed, Y unsigned + if (x.bits.as_signed < 0 || (uintmax_t)x.bits.as_signed < y.bits.as_unsigned) { + return mlib_less; + } else if ((uintmax_t)x.bits.as_signed > y.bits.as_unsigned) { + return mlib_greater; + } + } + } else { + if (!y.is_signed) { + // Both unsigned + if (x.bits.as_unsigned < y.bits.as_unsigned) { + return mlib_less; + } else if (x.bits.as_unsigned > y.bits.as_unsigned) { + return mlib_greater; + } + } else { + // X unsigned, Y signed + if (y.bits.as_signed < 0 || x.bits.as_unsigned > (uintmax_t)y.bits.as_signed) { + return mlib_greater; + } else if (x.bits.as_unsigned < (uintmax_t)y.bits.as_signed) { + return mlib_less; + } + } + } + return mlib_equal; +} + +/** + * @brief Test whether the given operand is within the range of some other integral type + * + * @param T A type specifier of the target integral type + * @param Operand the expression that is being inspected. + * + * @note This macro may evaluate the operand more than once + */ +#define mlib_in_range(T, Operand) \ + mlib_in_range((intmax_t)mlib_minof(T), (uintmax_t)mlib_maxof(T), mlib_upsize_integer(Operand)) +static inline bool(mlib_in_range)(intmax_t min_, uintmax_t max_, struct mlib_upsized_integer val) mlib_noexcept +{ + if (val.is_signed) { + return mlib_cmp(val.bits.as_signed, >=, min_) && mlib_cmp(val.bits.as_signed, <=, max_); + } else { + return mlib_cmp(val.bits.as_unsigned, >=, min_) && mlib_cmp(val.bits.as_unsigned, <=, max_); + } +} + +#endif // MLIB_CMP_H_INCLUDED diff --git a/bsonjs/common/mlib/config.h b/bsonjs/common/mlib/config.h new file mode 100644 index 0000000..a95f917 --- /dev/null +++ b/bsonjs/common/mlib/config.h @@ -0,0 +1,419 @@ +/** + * @file mlib/config.h + * @brief Provides utility macros + * @date 2024-08-29 + * + * @note This file is intented to be standalone-includable, with no dependencies + * other than the standard library and platform headers. This file (and other + * `mlib` files), are for internal use only, and should not be used in any public + * headers. + * + * @copyright Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MLIB_CONFIG_H_INCLUDED +#define MLIB_CONFIG_H_INCLUDED + +#ifndef _WIN32 +#include // Endian detection +#endif + +/** + * @brief A function-like macro that always expands to nothing + */ +#define MLIB_NOTHING(...) + +/** + * @brief A function macro that simply expands to its arguments unchanged + */ +#define MLIB_JUST(...) __VA_ARGS__ + +// Paste two tokens +#ifndef _MSC_VER +#define MLIB_PASTE(A, ...) _mlibPaste1(A, __VA_ARGS__) +#else +#define MLIB_PASTE(A, ...) MLIB_JUST(_mlibPaste1(A, __VA_ARGS__)) +#endif +// Paste three tokens +#define MLIB_PASTE_3(A, B, ...) MLIB_PASTE(A, MLIB_PASTE(B, __VA_ARGS__)) +// Paste four tokens +#define MLIB_PASTE_4(A, B, C, ...) MLIB_PASTE(A, MLIB_PASTE_3(B, C, __VA_ARGS__)) +// Paste five tokens +#define MLIB_PASTE_5(A, B, C, D, ...) MLIB_PASTE(A, MLIB_PASTE_4(B, C, D, __VA_ARGS__)) +#define _mlibPaste1(A, ...) A##__VA_ARGS__ + +/** + * @brief Convert the token sequence into a string after macro expansion + */ +#define MLIB_STR(...) _mlibStr(__VA_ARGS__) +#define _mlibStr(...) #__VA_ARGS__ + +#define MLIB_EVAL_32(...) MLIB_EVAL_16(MLIB_EVAL_16(__VA_ARGS__)) +#define MLIB_EVAL_16(...) MLIB_EVAL_8(MLIB_EVAL_8(__VA_ARGS__)) +#define MLIB_EVAL_8(...) MLIB_EVAL_4(MLIB_EVAL_4(__VA_ARGS__)) +#define MLIB_EVAL_4(...) MLIB_EVAL_2(MLIB_EVAL_2(__VA_ARGS__)) +#define MLIB_EVAL_2(...) MLIB_EVAL_1(MLIB_EVAL_1(__VA_ARGS__)) +#define MLIB_EVAL_1(...) __VA_ARGS__ + +// clang-format off +/** + * @brief Expand to 1 if given no arguments, otherwise 0. + * + * This could be done trivially using __VA_OPT__, but we need to work on + * older compilers. + */ +#define MLIB_IS_EMPTY(...) \ + _mlibIsEmpty_1( \ + /* Expands to '1' if __VA_ARGS__ contains any top-level commas */ \ + _mlibHasComma(__VA_ARGS__), \ + /* Expands to '1' if __VA_ARGS__ begins with a parenthesis, because \ + * that will cause an "invocation" of _mlibCommaIfParens, \ + * which immediately expands to a single comma. */ \ + _mlibHasComma(_mlibCommaIfParens __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 */ \ + _mlibHasComma(__VA_ARGS__ ()), \ + /* Expands to '1' if __VA_ARGS__ expands to nothing. */ \ + _mlibHasComma(_mlibCommaIfParens __VA_ARGS__ ())) +// Expand to 1 if the argument list has a comma. The weird definition is to support +// old MSVC's bad preprocessor +#define _mlibHasComma(...) \ + MLIB_JUST(_mlibPickSixteenth \ + MLIB_NOTHING("MSVC workaround") \ + (__VA_ARGS__, 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 +#define _mlibCommaIfParens(...) , + +/** + * @brief Expands to `1` if the given macro argument is a parenthesized group of + * tokens, otherwise `0` + */ +#define MLIB_IS_PARENTHESIZED(X) \ + _mlibHasComma(_mlibCommaIfParens X) + +/** + * @brief Pass a function-like macro name, inhibiting its expansion until the + * next pass: + * + * #define func_macro(x) x + * + * MLIB_DEFERRED(func_macro)(foo) // Expands to "func_macro(foo)", not "foo" + */ +#define MLIB_DEFERRED(MacroName) \ + /* Expand to the macro name: */ \ + MacroName \ + /*- + * Place a separator between the function macro name and whatever comes next + * in the file. Presumably, the next token will be the parens to invoke "MacroName", + * but this separator inhibits its expansion unless something else comes + * along to do another expansion pass + */ \ + MLIB_NOTHING("[separator]") + +/** + * A helper for isEmpty(): If given (0, 0, 0, 1), expands as: + * - first: _mlibHasComma(_mlibIsEmptyCase_0001) + * - then: _mlibHasComma(,) + * - then: 1 + * Given any other aruments: + * - first: _mlibHasComma(_mlibIsEmptyCase_) + * - then: 0 + */ +#define _mlibIsEmpty_1(_1, _2, _3, _4) \ + _mlibHasComma(MLIB_PASTE_5(_mlibIsEmptyCase_, _1, _2, _3, _4)) +#define _mlibIsEmptyCase_0001 , + +#define MLIB_IS_NOT_EMPTY(...) MLIB_PASTE (_mlibNotEmpty_, MLIB_IS_EMPTY (__VA_ARGS__)) +#define _mlibNotEmpty_1 0 +#define _mlibNotEmpty_0 1 +// clang-format on + +/** + * @brief If the argument expands to `0`, `false`, or nothing, expands to `0`. + * Otherwise expands to `1`. + */ +#define MLIB_BOOLEAN(...) MLIB_IS_NOT_EMPTY(MLIB_PASTE_3(_mlib, Bool_, __VA_ARGS__)) +#define _mlibBool_0 +#define _mlibBool_false +#define _mlibBool_ + +/** + * @brief A ternary macro. Expects three parenthesized argument lists in + * sequence. + * + * If the first argument list is a truthy value, expands to the second argument + * list. Otherwise, expands to the third argument list. The unused argument list + * is not expanded and is discarded. + */ +#define MLIB_IF_ELSE(...) MLIB_PASTE(_mlibIfElseBranch_, MLIB_BOOLEAN(__VA_ARGS__)) +#define _mlibIfElseBranch_1(...) __VA_ARGS__ MLIB_NOTHING +#define _mlibIfElseBranch_0(...) MLIB_JUST + +/** + * @brief Expands to an integer literal corresponding to the number of macro + * arguments. Supports up to fifteen arguments. + */ +#define MLIB_ARG_COUNT(...) \ + MLIB_IF_ELSE(MLIB_IS_EMPTY(__VA_ARGS__)) \ + (0)(_mlibPickSixteenth(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) +#define _mlibPickSixteenth(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) _16 + +/** + * @brief Expand to a call expression `Prefix##_argc_N(...)`, where `N` is the + * number of macro arguments. + * + * XXX: The `MLIB_JUST` forces an additional expansion pass that works around a + * bug in the old MSVC preprocessor, but is not required in a conforming preprocessor. + */ +#define MLIB_ARGC_PICK(Prefix, ...) MLIB_JUST(MLIB_ARGC_PASTE(Prefix, __VA_ARGS__)(__VA_ARGS__)) +#define MLIB_ARGC_PASTE(Prefix, ...) MLIB_PASTE_3(Prefix, _argc_, MLIB_ARG_COUNT(__VA_ARGS__)) + +#ifdef __cplusplus +#define mlib_is_cxx() 1 +#define mlib_is_not_cxx() 0 +#define MLIB_IF_CXX(...) __VA_ARGS__ +#define MLIB_IF_NOT_CXX(...) +#else +#define mlib_is_cxx() 0 +#define mlib_is_not_cxx() 1 +#define MLIB_IF_CXX(...) +#define MLIB_IF_NOT_CXX(...) __VA_ARGS__ +#endif + +#define MLIB_LANG_PICK MLIB_IF_ELSE(mlib_is_not_cxx()) + +/** + * @brief Use as the prefix of a braced initializer within C headers, allowing + * the initializer to appear as a compound-init in C and an equivalent braced + * aggregate-init in C++ + */ +#define mlib_init(T) MLIB_LANG_PICK((T))(T) + +/** + * @brief Expands to `noexcept` when compiled as C++, otherwise expands to + * nothing + */ +#define mlib_noexcept MLIB_IF_CXX(noexcept) + +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) +#define mlib_is_little_endian() (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) +#define mlib_is_little_endian() (__BYTE_ORDER == __LITTLE_ENDIAN) +#elif defined(_WIN32) +#define mlib_is_little_endian() 1 +#else +#error "Do not know how to detect endianness on this platform." +#endif + +// clang-format off +/** + * @brief Expands to a static assertion declaration. + * + * When supported, this can be replaced with `_Static_assert` or `static_assert` + */ +#define mlib_static_assert(...) MLIB_ARGC_PICK (_mlib_static_assert, __VA_ARGS__) +#define _mlib_static_assert_argc_1(Expr) \ + _mlib_static_assert_argc_2 ((Expr), "Static assertion failed") +#define _mlib_static_assert_argc_2(Expr, Msg) \ + extern int \ + MLIB_PASTE (_mlib_static_assert_placeholder, __COUNTER__)[(Expr) ? 2 : -1] \ + MLIB_IF_GNU_LIKE (__attribute__ ((unused))) +// clang-format on + +#define mlib_extern_c_begin() MLIB_IF_CXX(extern "C" {) mlib_static_assert(1, "") +#define mlib_extern_c_end() MLIB_IF_CXX( \ + }) mlib_static_assert(1, "") + +#ifdef __GNUC__ +#define mlib_is_gnu_like() 1 +#ifdef __clang__ +#define mlib_is_gcc() 0 +#define mlib_is_clang() 1 +#else +#define mlib_is_gcc() 1 +#define mlib_is_clang() 0 +#endif +#define mlib_is_msvc() 0 +#elif defined(_MSC_VER) +#define mlib_is_gnu_like() 0 +#define mlib_is_clang() 0 +#define mlib_is_gcc() 0 +#define mlib_is_msvc() 1 +#endif + +#if defined(_WIN32) +#define mlib_is_win32() 1 +#define mlib_is_unix() 0 +#else +#define mlib_is_unix() 1 +#define mlib_is_win32() 0 +#endif + +#define MLIB_IF_CLANG(...) MLIB_IF_ELSE(mlib_is_clang())(__VA_ARGS__)(MLIB_NOTHING(#__VA_ARGS__)) +#define MLIB_IF_GCC(...) MLIB_IF_ELSE(mlib_is_gcc())(__VA_ARGS__)(MLIB_NOTHING(#__VA_ARGS__)) +#define MLIB_IF_GNU_LIKE(...) MLIB_IF_GCC(__VA_ARGS__) MLIB_IF_CLANG(__VA_ARGS__) MLIB_NOTHING(#__VA_ARGS__) +#define MLIB_IF_UNIX_LIKE(...) MLIB_IF_ELSE(mlib_is_unix())(__VA_ARGS__)(MLIB_NOTHING(#__VA_ARGS__)) + +// note: Bug on GCC preprocessor prevents us from using if/else trick to omit MSVC code +#if mlib_is_msvc() +#define MLIB_IF_MSVC(...) __VA_ARGS__ +#define mlib_pragma(...) __pragma(__VA_ARGS__) mlib_static_assert(1, "") +#else +#define MLIB_IF_MSVC(...) MLIB_NOTHING(#__VA_ARGS__) +#define mlib_pragma(...) _Pragma(#__VA_ARGS__) mlib_static_assert(1, "") +#endif + +#define MLIB_PRAGMA_IF_CLANG(...) MLIB_IF_CLANG(_Pragma(#__VA_ARGS__)) +#define MLIB_PRAGMA_IF_GCC(...) MLIB_IF_GCC(_Pragma(#__VA_ARGS__)) +#define MLIB_PRAGMA_IF_GNU_LIKE(...) MLIB_IF_GNU_LIKE(_Pragma(#__VA_ARGS__)) +#define MLIB_PRAGMA_IF_UNIX_LIKE(...) MLIB_IF_UNIX_LIKE(_Pragma(#__VA_ARGS__)) +#define MLIB_PRAGMA_IF_MSVC(...) MLIB_IF_MSVC(__pragma(__VA_ARGS__)) + +#define MLIB_FUNC MLIB_IF_GNU_LIKE(__func__) MLIB_IF_MSVC(__FUNCTION__) + +#define mlib_diagnostic_push() \ + MLIB_IF_GNU_LIKE(mlib_pragma(GCC diagnostic push);) \ + MLIB_IF_MSVC(mlib_pragma(warning(push));) \ + mlib_static_assert(1, "") + +#define mlib_diagnostic_pop() \ + MLIB_IF_GNU_LIKE(mlib_pragma(GCC diagnostic pop);) \ + MLIB_IF_MSVC(mlib_pragma(warning(pop));) \ + mlib_static_assert(1, "") + +#define mlib_gcc_warning_disable(Warning) \ + MLIB_IF_GCC(mlib_pragma(GCC diagnostic ignored Warning);) \ + mlib_static_assert(1, "") + +#define mlib_gnu_warning_disable(Warning) \ + MLIB_IF_GNU_LIKE(mlib_pragma(GCC diagnostic ignored Warning);) \ + mlib_static_assert(1, "") + +#define mlib_msvc_warning(...) \ + MLIB_IF_MSVC(mlib_pragma(warning(__VA_ARGS__));) \ + mlib_static_assert(1, "") + +/** + * @brief Attribute macro that forces the function to be inlined at all call sites. + * + * Don't use this unless you really know that you need it, lest you generate code + * bloat when the compiler's heuristics would do a better job. + */ +#define mlib_always_inline MLIB_IF_GNU_LIKE(__attribute__((always_inline)) inline) MLIB_IF_MSVC(__forceinline) + +// Annotate a variable as thread-local +#define mlib_thread_local MLIB_IF_GNU_LIKE(__thread) MLIB_IF_MSVC(__declspec(thread)) + +// Annotate an entiry that might be unused +#define mlib_maybe_unused MLIB_IF_GNU_LIKE(__attribute__((unused))) + +// clang-format off +/** + * @brief Expand to `1` if the current build configuration matches the given token. + * + * If the token is not a known valid build configuration, generates a compilation + * error (check your spelling!) + * + * Requires that `_MLIB_BUILD_CONFIG` is defined, otherwise always expands to `0` + */ +#define mlib_build_config_is(Config) \ + /* If `Config` is a recognized config, this line will disappear, */ \ + /* other wise it will be a "call to undefined macro": */ \ + MLIB_PASTE_4 (_mlibTestBuildConfig_, Config, _, Config) () \ + /* If `Config` is the same token as `_MLIB_BUILD_CONFIG`, this will */ \ + /* expand to `1`, otherwise it will expand to `0` */ \ + MLIB_IS_EMPTY (MLIB_PASTE_4 (_mlibTestBuildConfig_, Config, _, _MLIB_BUILD_CONFIG) ()) +// clang-format on +// Known build configurations: +#define _mlibTestBuildConfig_Release_Release() +#define _mlibTestBuildConfig_Debug_Debug() +#define _mlibTestBuildConfig_RelWithDebInfo_RelWithDebInfo() +#define _mlibTestBuildConfig_MinSizeRel_MinSizeRel() + +/** + * @brief Emit a _Pragma that will disable warnings about the use of deprecated entities. + */ +#define mlib_disable_deprecation_warnings() \ + mlib_gnu_warning_disable("-Wdeprecated-declarations"); \ + mlib_msvc_warning(disable : 4996) + +/** + * @brief Function-like macro that expands to `1` if we are certain that we are + * compiling with optimizations enabled. + * + * This may yield `0` if we cannot determine whether optimization is turned on. + * + * This macro should be used with care, as different translation units can see different values, + * but still be linked together in the final program. Beware generating ODR violations. + */ +#define mlib_is_optimized_build() _mlibIsOptimizedBuild() + +#if mlib_build_config_is(Release) || mlib_build_config_is(RelWithDebInfo) || mlib_build_config_is(MinSizeRel) || \ + (defined(__OPTIMIZE__) && __OPTIMIZE__) +// Preproc definition __OPTIMIZE__set by GCC ang Clang when the optimizer is enabled. +// MSVC has no such definition, so we rely on CMake to tell us when we are compiling in release mode +#define _mlibIsOptimizedBuild() 1 +#else +#define _mlibIsOptimizedBuild() 0 +#endif + +#if mlib_is_gnu_like() +#define mlib_have_typeof() 1 +#elif defined _MSC_VER && _MSC_VER >= 1939 && !__cplusplus +// We can __typeof__ in MSVC 19.39+ +#define mlib_have_typeof() 1 +#else +#define mlib_have_typeof() 0 +#endif + +/** + * @brief Equivalent to C23's `typeof()`, if it is supported by the current compiler. + * + * This expands to `__typeof__`, which is supported even on newer MSVC compilers, + * even when not in C23 mode. + */ +#define mlib_typeof(...) MLIB_IF_ELSE(mlib_have_typeof())(__typeof__)(__mlib_typeof_is_not_supported)(__VA_ARGS__) + +/** + * @brief Disable warnings for constant conditional expressions. + */ +#define mlib_disable_constant_conditional_expression_warnings() mlib_msvc_warning(disable : 4127) + +/** + * @brief Disable warnings for potentially unused parameters. + */ +#define mlib_disable_unused_parameter_warnings() \ + MLIB_IF_GNU_LIKE(mlib_gnu_warning_disable("-Wunused-parameter");) \ + MLIB_IF_MSVC(mlib_msvc_warning(disable : 4100);) mlib_static_assert(1, "") + +#if mlib_is_clang() +#define mlib_printf_attribute(f, v) __attribute__((format(printf, f, v))) +#elif mlib_is_gcc() +#define mlib_printf_attribute(f, v) __attribute__((format(gnu_printf, f, v))) +#else +#define mlib_printf_attribute(f, v) +#endif + +/** + * @brief Annotate a boolean expression as "likely to be true" to guide the optimizer. + * Use this very sparingly. + */ +#define mlib_likely(...) MLIB_IF_ELSE(mlib_is_gnu_like())(__builtin_expect(!!(__VA_ARGS__), 1))((__VA_ARGS__)) +/** + * @brief Annotate a boolean expression as "likely to be untrue" to guide the optimizer. + * Use this very sparingly. + */ +#define mlib_unlikely(...) MLIB_IF_ELSE(mlib_is_gnu_like())(__builtin_expect(!!(__VA_ARGS__), 0))((__VA_ARGS__)) + +#endif // MLIB_CONFIG_H_INCLUDED diff --git a/bsonjs/common/mlib/duration.h b/bsonjs/common/mlib/duration.h new file mode 100644 index 0000000..f2496fe --- /dev/null +++ b/bsonjs/common/mlib/duration.h @@ -0,0 +1,399 @@ +/** + * @file mlib/duration.h + * @brief Duration types and functions + * @date 2025-04-17 + * + * This file contains types and functions for working with a "duration" type, + * which represents an elapsed amount of time, possibly negative. + * + * The type `mlib_duration_rep_t` is a typedef of the intregral type that is + * used to represent duration units. + * + * The `mlib_duration` is a trivial object that represents a duration of time. + * The internal representation should not be inspected outside of this file. + * + * @copyright Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MLIB_DURATION_H_INCLUDED +#define MLIB_DURATION_H_INCLUDED + +#include +#include +#include +#include + +#include +#include + +mlib_extern_c_begin(); + +/** + * @brief The integral type used to represent a count of units of time. + */ +typedef int64_t mlib_duration_rep_t; + +/** + * @brief Represents a duration of time, either positive, negative, or zero. + * + * @note A zero-initialized (static initialized) duration represents the zero + * duration (no elapsed time) + * + * @note The time representation is intended to be abstract, and should be + * converted to concrete units of time by calling the `_count` functions. + */ +typedef struct mlib_duration { + /** + * @brief The integral representation of the duration. + * + * Do not read or modify this field except to zero-initialize it. + */ + mlib_duration_rep_t _rep; +} mlib_duration; + +/** + * @brief A macro that expands to the maximum positive duration + */ +#define mlib_duration_max() (mlib_init(mlib_duration){mlib_maxof(mlib_duration_rep_t)}) +/** + * @brief A macro that expands to the minimum duration (a negative duration) + */ +#define mlib_duration_min() (mlib_init(mlib_duration){mlib_minof(mlib_duration_rep_t)}) + +/** + * @brief Obtain the count of microseconds represented by the duration (round + * toward zero) + */ +static inline mlib_duration_rep_t +mlib_microseconds_count(const mlib_duration dur) mlib_noexcept +{ + return dur._rep; +} + +/** + * @brief Obtain the count of milliseconds represented by the duration (round + * toward zero) + */ +static inline mlib_duration_rep_t +mlib_milliseconds_count(const mlib_duration dur) mlib_noexcept +{ + return mlib_microseconds_count(dur) / 1000; +} + +/** + * @brief Obtain the count of seconds represented by the duration (rounded + * toward zero) + */ +static inline mlib_duration_rep_t +mlib_seconds_count(const mlib_duration dur) mlib_noexcept +{ + return mlib_milliseconds_count(dur) / 1000; +} + +/** + * @brief Duration creation and manipulation shorthands + * + * This function-like macro is used to create and manipulate durations on-the-fly. + * It can be called with the following syntaxes: + * + * - `mlib_duration()` (no arguments) + * creates a zero-valued duration + * - `mlib_duration()` + * copies the duration object `` + * - `mlib_duration(, )` + * Creates a duration of `` instances of ``. + * - `mlib_duration(, , )` + * Manipulates a duration according to ``. + * + * In the above, `` may be a parenthesized `mlib_duration` argument list or a + * duration object; `` must be an integral expression and `` is a + * unit suffix identifer (see: `mlib_duration_with_unit`) to create a duration + * of `` instances of ``, and `` is one of: + * + * - `plus`/`minus` to add/subtract two durations. + * - `mul`/`div` to multiply/divide a duration by a scalar factor. + * - `min`/`max` to get the minimum/maximum between two durations. + * + * All duration arithmetic/conversion operations use well-defined saturating + * arithmetic, and never wrap or trap. + */ +#define mlib_duration(...) MLIB_EVAL_16(_mlibDurationMagic(__VA_ARGS__)) +#define _mlibDurationMagic(...) \ + MLIB_DEFERRED(MLIB_ARGC_PASTE(_mlib_duration, __VA_ARGS__)) \ + (__VA_ARGS__) +// Wraps a `` argument, and expands to the magic only if it is parenthesized +#define _mlibDurationArgument(X) \ + /* If given a parenthesized expression, act as an invocation of `mlib_duration() */ \ + MLIB_IF_ELSE(MLIB_IS_PARENTHESIZED(X)) \ + /* then: */ (_mlibDurationMagic X) /* else: */ (X) + +// Wrap a macro argument that should support the duration DSL +#define mlib_duration_arg(X) MLIB_EVAL_16(_mlibDurationArgument(X)) + +// Zero arguments, just return a zero duration: +#define _mlib_duration_argc_0() (mlib_init(mlib_duration){0}) +// One argument, just copy the duration. Passing through a function forces the type to be correct +#define _mlib_duration_argc_1(D) _mlibDurationCopy(D) +// Two arguments, the second arg is a unit suffix: +#define _mlib_duration_argc_2(Count, Unit) mlib_duration_with_unit(Count, Unit) +// Three arguments, an infix operation: +#define _mlib_duration_argc_3(Duration, Operator, Operand) \ + MLIB_DEFERRED(MLIB_PASTE(_mlibDurationInfixOperator_, Operator)) \ + (Duration, Operand) + +// By-value copy a duration +static inline mlib_duration +_mlibDurationCopy(mlib_duration d) +{ + return d; +} + +// Duration scalar multiply +#define _mlibDurationInfixOperator_mul(LHS, Fac) \ + _mlibDurationMultiply(_mlibDurationArgument(LHS), mlib_upsize_integer(Fac)) +static inline mlib_duration +_mlibDurationMultiply(const mlib_duration dur, mlib_upsized_integer fac) mlib_noexcept +{ + mlib_duration ret = {0}; + const bool overflowed = fac.is_signed ? mlib_mul(&ret._rep, dur._rep, fac.bits.as_signed) + : mlib_mul(&ret._rep, dur._rep, fac.bits.as_unsigned); + if (overflowed) { + if ((dur._rep < 0) != (fac.is_signed && fac.bits.as_signed < 0)) { + // Different signs: Neg × Pos = Neg + ret = mlib_duration_min(); + } else { + // Same signs: Pos × Pos = Pos + // Neg × Neg = Pos + ret = mlib_duration_max(); + } + } + return ret; +} + +// Duration scalar divide +#define _mlibDurationInfixOperator_div(LHS, Div) \ + _mlibDurationDivide(_mlibDurationArgument(LHS), mlib_upsize_integer(Div)) +static inline mlib_duration +_mlibDurationDivide(mlib_duration a, mlib_upsized_integer div) mlib_noexcept +{ + mlib_check(div.bits.as_unsigned, neq, 0); + if ((div.is_signed && div.bits.as_signed == -1) // + && a._rep == mlib_minof(mlib_duration_rep_t)) { + // MIN / -1 is UB, but the saturating result is the max + a = mlib_duration_max(); + } else { + if (div.is_signed) { + a._rep /= div.bits.as_signed; + } else { + a._rep = (mlib_duration_rep_t)((uintmax_t)a._rep / div.bits.as_unsigned); + } + } + return a; +} + +// Duration addition +#define _mlibDurationInfixOperator_plus(LHS, RHS) \ + _mlibDurationAdd(_mlibDurationArgument(LHS), _mlibDurationArgument(RHS)) +static inline mlib_duration +_mlibDurationAdd(const mlib_duration a, const mlib_duration b) mlib_noexcept +{ + mlib_duration ret = {0}; + if (mlib_add(&ret._rep, a._rep, b._rep)) { + if (a._rep > 0) { + ret = mlib_duration_max(); + } else { + ret = mlib_duration_min(); + } + } + return ret; +} + +// Duration subtraction +#define _mlibDurationInfixOperator_minus(LHS, RHS) \ + _mlibDurationSubtract(_mlibDurationArgument(LHS), _mlibDurationArgument(RHS)) +static inline mlib_duration +_mlibDurationSubtract(const mlib_duration a, const mlib_duration b) mlib_noexcept +{ + mlib_duration ret = {0}; + if (mlib_sub(&ret._rep, a._rep, b._rep)) { + if (a._rep < 0) { + ret = mlib_duration_min(); + } else { + ret = mlib_duration_max(); + } + } + return ret; +} + +#define _mlibDurationInfixOperator_min(Duration, RHS) \ + _mlibDurationMinBetween(_mlibDurationArgument(Duration), _mlibDurationArgument(RHS)) +static inline mlib_duration +_mlibDurationMinBetween(mlib_duration lhs, mlib_duration rhs) +{ + if (lhs._rep < rhs._rep) { + return lhs; + } + return rhs; +} + +#define _mlibDurationInfixOperator_max(Duration, RHS) \ + _mlibDurationMaxBetween(_mlibDurationArgument(Duration), _mlibDurationArgument(RHS)) +static inline mlib_duration +_mlibDurationMaxBetween(mlib_duration lhs, mlib_duration rhs) +{ + if (lhs._rep > rhs._rep) { + return lhs; + } + return rhs; +} + +/** + * @brief Create a duration object from a count of some unit of time + * + * @param Count An integral expression + * @param Unit A unit suffix identifier, must be one of: + * + * - `ns` (nanoseconds) + * - `us` (microseconds) + * - `ms` (milliseconds) + * - `s` (seconds) + * - `mn` (minutes) + * - `h` (hours) + * + * Other unit suffixes will generate a compile-time error + */ +#define mlib_duration_with_unit(Count, Unit) \ + MLIB_PASTE(_mlibCreateDurationFromUnitCount_, Unit)(mlib_upsize_integer(Count)) + +static inline mlib_duration +_mlibCreateDurationFromUnitCount_us(const mlib_upsized_integer n) mlib_noexcept +{ + mlib_duration ret = mlib_duration(); + if (n.is_signed) { + // The duration rep is the same as the signed max type, so we don't need to do any + // special arithmetic to encode it + mlib_static_assert(sizeof(mlib_duration_rep_t) == sizeof(n.bits.as_signed)); + ret._rep = mlib_assert_narrow(mlib_duration_rep_t, n.bits.as_signed); + } else { + if (mlib_narrow(&ret._rep, n.bits.as_unsigned)) { + // Unsigned value is too large to fit in our signed repr, so just use the max repr + ret = mlib_duration_max(); + } + } + return ret; +} + +static inline mlib_duration +_mlibCreateDurationFromUnitCount_ns(mlib_upsized_integer n) mlib_noexcept +{ + // We encode as a count of microseconds, so we lose precision here. + if (n.is_signed) { + n.bits.as_signed /= 1000; + } else { + n.bits.as_unsigned /= 1000; + } + return _mlibCreateDurationFromUnitCount_us(n); +} + +static inline mlib_duration +_mlibCreateDurationFromUnitCount_ms(const mlib_upsized_integer n) mlib_noexcept +{ + return mlib_duration(_mlibCreateDurationFromUnitCount_us(n), mul, 1000); +} + +static inline mlib_duration +_mlibCreateDurationFromUnitCount_s(const mlib_upsized_integer n) +{ + return mlib_duration(_mlibCreateDurationFromUnitCount_us(n), mul, 1000 * 1000); +} + +static inline mlib_duration +_mlibCreateDurationFromUnitCount_mn(const mlib_upsized_integer n) +{ + return mlib_duration(_mlibCreateDurationFromUnitCount_us(n), mul, 60 * 1000 * 1000); +} + +static inline mlib_duration +_mlibCreateDurationFromUnitCount_h(const mlib_upsized_integer n) +{ + return mlib_duration(_mlibCreateDurationFromUnitCount_mn(n), mul, 60); +} + +/** + * @brief Compare two durations + * + * @retval <0 If `a` is less-than `b` + * @retval >0 If `b` is less-than `a` + * @retval 0 If `a` and `b` are equal durations + * + * @note This is a function-like macro that can be called with an infix operator + * as the second argument to do natural duration comparisons: + * + * ``` + * mlib_duration_cmp(, , ) + * ``` + * + * Where each `` should be an arglist for @see mlib_duration + */ +static inline enum mlib_cmp_result +mlib_duration_cmp(const mlib_duration a, const mlib_duration b) mlib_noexcept +{ + return mlib_cmp(a._rep, b._rep); +} + +#define mlib_duration_cmp(...) MLIB_ARGC_PICK(_mlibDurationCmp, __VA_ARGS__) +#define _mlibDurationCmp_argc_2 mlib_duration_cmp +#define _mlibDurationCmp_argc_3(Left, Op, Right) \ + (mlib_duration_cmp(mlib_duration_arg(Left), mlib_duration_arg(Right)) Op 0) + +/** + * @brief Obtain an mlib_duration that corresponds to a `timespec` value + * + * @note The `timespec` type may represent times outside of the range of, or + * more precise than, what is representable in `mlib_duration`. In such case, + * the returned duration will be the nearest representable duration, rounded + * toward zero. + */ +static inline mlib_duration +mlib_duration_from_timespec(const struct timespec ts) mlib_noexcept +{ + return mlib_duration((ts.tv_sec, s), plus, (ts.tv_nsec, ns)); +} + +/** + * @brief Create a C `struct timespec` that corresponds to the given duration + * + * @param d The duration to be converted + * @return struct timespec A timespec that represents the same durations + */ +static inline struct timespec +mlib_duration_to_timespec(const mlib_duration d) mlib_noexcept +{ + // Number of full seconds in the duration + const mlib_duration_rep_t n_full_seconds = mlib_seconds_count(d); + // Duration with full seconds removed + const mlib_duration usec_part = mlib_duration(d, minus, (n_full_seconds, s)); + // Number of microseconds in the duration, minus all full seconds + const mlib_duration_rep_t n_remaining_microseconds = mlib_microseconds_count(usec_part); + // Compute the number of nanoseconds: + const int32_t n_nsec = mlib_assert_mul(int32_t, n_remaining_microseconds, 1000); + struct timespec ret; + ret.tv_sec = n_full_seconds; + ret.tv_nsec = n_nsec; + return ret; +} + +mlib_extern_c_end(); + +#endif // MLIB_DURATION_H_INCLUDED diff --git a/bsonjs/common/mlib/intencode.h b/bsonjs/common/mlib/intencode.h new file mode 100644 index 0000000..f26cae5 --- /dev/null +++ b/bsonjs/common/mlib/intencode.h @@ -0,0 +1,350 @@ +/** + * @file mlib/intencode.h + * @brief Integer encoding functions + * @date 2025-01-31 + * + * @copyright Copyright (c) 2025 + * + * 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. + */ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +/** + * @brief Decode an unsigned 32-bit little-endian integer from a memory buffer + */ +static inline uint32_t +mlib_read_u32le(const void *buf) +{ + uint32_t ret = 0; + if (mlib_is_little_endian()) { + // Optimize: The platform uses a LE encoding already + memcpy(&ret, buf, sizeof ret); + } else { + // Portable decode of an LE integer + const uint8_t *cptr = (const uint8_t *)buf; + mlib_foreach_urange (i, sizeof ret) { + ret <<= 8; + ret |= cptr[(sizeof ret) - i - 1]; + } + } + return ret; +} + +/** + * @brief Decode an signed 32-bit little-endian integer from a memory buffer + */ +static inline int32_t +mlib_read_i32le(const void *buf) +{ + const uint32_t u = mlib_read_u32le(buf); + int32_t r; + memcpy(&r, &u, sizeof r); + return r; +} + +/** + * @brief Decode an unsigned 64-bit little-endian integer from a memory buffer + */ +static inline uint64_t +mlib_read_u64le(const void *buf) +{ + uint64_t ret = 0; + if (mlib_is_little_endian()) { + // Optimize: The platform uses a LE encoding already + memcpy(&ret, buf, sizeof ret); + } else { + // Portable decode of an LE integer + const uint8_t *cptr = (const uint8_t *)buf; + mlib_foreach_urange (i, sizeof ret) { + ret <<= 8; + ret |= cptr[(sizeof ret) - i - 1]; + } + } + return ret; +} + +/** + * @brief Decode an signed 64-bit little-endian integer from a memory buffer + */ +static inline int64_t +mlib_read_i64le(const void *buf) +{ + const uint64_t u = mlib_read_u64le(buf); + int64_t r; + memcpy(&r, &u, sizeof r); + return r; +} + +/** + * @brief Write an unsigned 32-bit little-endian integer into a destination + * + * @return void* The address after the written value + */ +static inline void * +mlib_write_u32le(void *out, const uint32_t value) +{ + uint8_t *o = (uint8_t *)out; + if (mlib_is_little_endian()) { + memcpy(o, &value, sizeof value); + return o + sizeof value; + } + mlib_foreach_urange (i, sizeof value) { + *o++ = (value >> (8u * i)) & 0xffu; + } + return o; +} + +/** + * @brief Write a signed 32-bit little-endian integer into a destination + * + * @return void* The address after the written value + */ +static inline void * +mlib_write_i32le(void *out, int32_t value) +{ + return mlib_write_u32le(out, (uint32_t)value); +} + +/** + * @brief Write an unsigned 64-bit little-endian integer into a destination + * + * @return void* The address after the written value + */ +static inline void * +mlib_write_u64le(void *out, const uint64_t value) +{ + uint8_t *o = (uint8_t *)out; + if (mlib_is_little_endian()) { + memcpy(o, &value, sizeof value); + return o + sizeof value; + } + mlib_foreach_urange (i, sizeof value) { + *o++ = (value >> (8u * i)) & 0xffu; + } + return o; +} + +/** + * @brief Write an signed 64-bit little-endian integer into a destination + * + * @return void* The address after the written value + */ +static inline void * +mlib_write_i64le(void *out, int64_t value) +{ + return mlib_write_u64le(out, (uint64_t)value); +} + +/** + * @brief Write a little-endian 64-bit floating point (double) to the given + * memory location + * + * @return void* The address after the written value. + */ +static inline void * +mlib_write_f64le(void *out, double d) +{ + mlib_static_assert(sizeof(double) == sizeof(uint64_t)); + uint64_t bits; + memcpy(&bits, &d, sizeof d); + return mlib_write_u64le(out, bits); +} + +/** + * @brief Decode a 64-bit natural number + * + * @param in The input string to be decoded. Does not support a sign or base prefix! + * @param base The base to be decoded. Must not be zero! + * @param out Pointer that receives the decoded value + * @return int A result code for the operation. + * + * See `mlib_i64_parse` for more details. + */ +static inline int +mlib_nat64_parse(mstr_view in, unsigned base, uint64_t *out) +{ + if (in.len == 0) { + // Empty string is not valid + return EINVAL; + } + + // Accummulate into this value: + uint64_t value = 0; + // Whether any operation in the parse overflowed the integer value + bool did_overflow = false; + // Loop until we have consumed the full string, or encounter an invalid digit + while (in.len) { + // Shift place value for another digit + did_overflow = mlib_mul(&value, base) || did_overflow; + // Case-fold for alpha digits + int32_t digit = mlib_latin_tolower(in.data[0]); + unsigned digit_value = 0; + // Only standard digits + if (digit >= '0' && digit <= '9') { + // Normal digit + digit_value = (unsigned)(digit - '0'); + } else if (digit >= 'a' && digit <= 'z') { + // Letter digits + digit_value = (unsigned)(digit - 'a') + 10; + } else { + // Not a valid alnum digit + return EINVAL; + } + if (digit_value >= base) { + // The digit value is out-of-range for our chosen base + return EINVAL; + } + // Accumulate the new digit value + did_overflow = mlib_add(&value, digit_value) || did_overflow; + // Jump to the next digit in the string + in = mstr_substr(in, 1); + } + + if (did_overflow) { + return ERANGE; + } + + (void)(out && (*out = value)); + return 0; +} + +/** + * @brief Parse a string as a 64-bit signed integer + * + * @param in The string of digits to be parsed. + * @param base Optional: The base to use for parsing. Use "0" to infer the base. + * @param out Optional storage for an int64 value to be updated with the result + * @return int Returns an errno value for the parse + * + * - A value of `0` indicates that the parse was successful. + * - A value of `EINVAL` indicates that the input string is not a valid + * representation of an integer. + * - A value of `ERANGE` indicates that the input string is a valid integer, + * but the actual encoded value cannot be represented in an `int64_t` + * - If the parse fails (returns non-zero), then the value at `*out` will remain + * unmodified. + * + * This differs from `strtoll` in that it requires that the entire string be + * parsed as a valid integer. If parsing stops early, then the result will indicate + * an error of EINVAL. + */ +static inline int +mlib_i64_parse(mstr_view in, unsigned base, int64_t *out) +{ + if (in.len == 0) { + // Empty string is not a valid integer + return EINVAL; + } + // Parse the possible sign prefix + int sign = 1; + // Check for a "+" + if (in.data[0] == '+') { + // Just a plus. Drop it and do nothing with it. + in = mstr_substr(in, 1); + } + // Check for a negative prefix + else if (in.data[0] == '-') { + // Negative sign. We'll negate the value later. + in = mstr_substr(in, 1); + sign = -1; + } + + // Infer the base value, if we have one + if (base == 0) { + if (in.len && in.data[0] == '0') { + if (in.len > 1) { + if (mlib_latin_tolower(in.data[1]) == 'x') { + // Hexadecimal + base = 16; + in = mstr_substr(in, 2); + } else if (mlib_latin_tolower(in.data[1]) == 'o') { + // Octal + base = 8; + in = mstr_substr(in, 2); + } else if (mlib_latin_tolower(in.data[1]) == 'b') { + // Binary + base = 2; + in = mstr_substr(in, 2); + } + } + if (base == 0) { + // Other: Octal with a single "0" prefix. Don't trim this, because + // it may be a literal "0" + base = 8; + } + } else { + // No '0' prefix. Treat it as decimal + base = 10; + } + } + + // Try to parse the natural number now that we have removed all prefixes and + // have a non-zero base. + uint64_t nat; + int rc = mlib_nat64_parse(in, base, &nat); + if (rc) { + return rc; + } + + // Try to narrow from the u64 to i64 and apply the sign. This must be done as + // one operation because of the pathological case of parsing INT64_MIN + int64_t i64 = 0; + if (mlib_mul(&i64, nat, sign)) { + return ERANGE; + } + + (void)(out && (*out = i64)); + return 0; +} + +#define mlib_i64_parse(...) MLIB_ARGC_PICK(_mlib_i64_parse, __VA_ARGS__) +#define _mlib_i64_parse_argc_2(S, Ptr) _mlib_i64_parse_argc_3((S), 0, (Ptr)) +#define _mlib_i64_parse_argc_3(S, Base, Ptr) mlib_i64_parse(mstr_view_from((S)), Base, Ptr) + +/** + * @brief Parse a 32-bit integer from a string. + * + * See `mlib_i64_parse` for more details. + */ +static inline int +mlib_i32_parse(mstr_view in, unsigned base, int32_t *out) +{ + int64_t tmp; + int ec = mlib_i64_parse(in, base, &tmp); + if (ec) { + // Failed to parse the int64 value. + return ec; + } + // Attempt to narrow to a 32-bit value + int32_t i32 = 0; + if (mlib_narrow(&i32, tmp)) { + // Value is out-of-range + return ERANGE; + } + // Success + (void)(out && (*out = i32)); + return 0; +} + +#define mlib_i32_parse(...) MLIB_ARGC_PICK(_mlib_i32_parse, __VA_ARGS__) +#define _mlib_i32_parse_argc_2(S, Ptr) _mlib_i32_parse_argc_3((S), 0, (Ptr)) +#define _mlib_i32_parse_argc_3(S, Base, Ptr) mlib_i32_parse(mstr_view_from((S)), Base, Ptr) diff --git a/bsonjs/common/mlib/intutil.h b/bsonjs/common/mlib/intutil.h new file mode 100644 index 0000000..bd39978 --- /dev/null +++ b/bsonjs/common/mlib/intutil.h @@ -0,0 +1,145 @@ +/** + * @file mlib/intutil.h + * @brief Integer utilities + * @date 2025-01-28 + * + * @copyright Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MLIB_INTUTIL_H_INCLUDED +#define MLIB_INTUTIL_H_INCLUDED + +#include + +#include +#include +#include + +/** + * @brief Given an integral type, evaluates to `true` if that type is signed, + * otherwise `false` + */ +#define mlib_is_signed(T) (!((T)(-1) > 0)) + +/** + * @brief Like `sizeof`, but returns the number of bits in the object representation + */ +#define mlib_bitsizeof(T) ((sizeof(T)) * ((size_t)CHAR_BIT)) + +// clang-format off +/** + * @brief Generate a mask of contiguous bits. + * + * @param NumOnes The non-negative number of contiguous 1 bits + * @param NumZeros The non-negative number of contiguous 0 bits to set in the low position + * + * The generated mask is of the form: + * + * NumZeros + * │ + * ┌┴─┐ + * │ │ + * `0..0 1..1 0..0` + * │ │ + * └┬─┘ + * │ + * NumOnes + * + * Explain the arithmetic below: + * + * 1. `ones = 0b1111...` : All high bits + * 2. `tmp = ones >> (NumOnes - num_bits_of(ones))` : Truncate to the number of 1s we want + * 3. `res = tmp << NumZeros` : Add the 0s in the low position + */ +#define mlib_bits(NumOnes, NumZeros) ( \ + ((NumOnes) \ + ? (~UINTMAX_C(0) >> ((mlib_bitsizeof(uintmax_t) - (uintmax_t)(NumOnes)))) \ + : 0) \ + << ((uintmax_t)(NumZeros))) + +/** + * @brief Given an integral type, yield an integral constant value representing + * the maximal value of that type. + */ +#define mlib_maxof(T) \ + ((T) (mlib_is_signed (T) \ + ? ((T) mlib_bits(mlib_bitsizeof(T) - 1u, 0)) \ + : ((T) mlib_bits(mlib_bitsizeof(T), 0)))) + +/** + * @brief Given an integral type, yield an integral constant value for the + * minimal value of that type. + */ +#define mlib_minof(T) \ + ((T) (!mlib_is_signed (T) \ + ? (T) 0 \ + : (T) mlib_bits(1, mlib_bitsizeof(T) - 1u))) +// clang-format on + +/** + * @brief A container for an integer that has been "scaled up" to maximum precision + * + * Don't create this manually. Instead, use `mlib_upsize_integer` to do it automatically + */ +typedef struct mlib_upsized_integer { + union { + // The signed value of the integer + intmax_t as_signed; + // The unsigned value of the integer + uintmax_t as_unsigned; + } bits; + // Whether the upscaled integer bits should be treated as a two's complement signed integer + bool is_signed; +} mlib_upsized_integer; + +// clang-format off +/** + * @brief Create an "upsized" version of an integer, normalizing all integral + * values into a single type so that we can deduplicate functions that operate + * on disparate integer types. + * + * Details: The integer is upcast into the maximum precision integer type (intmax_t). If + * the operand is smaller than `intmax_t`, we assume that casting to the signed `intmax_t` + * is always safe, even if the operand is unsigned, since e.g. a u32 can always be cast to + * an i64 losslessly. + * + * If the integer to upcast is the same size as `intmax_t`, we need to decide whether to store + * it as unsigned. The expression `(_mlibGetOne(Value)) - 2 < 1` will be `true` iff the operand is signed, + * otherwise false. If the operand is signed, we can safely cast to `intmax_t` (it probably already + * is of that type), otherwise, we cast to `uintmax_t` and the returned `mlib_upsized_integer` will + * indicate that the stored value is unsigned. The expression `1 - 2 < 1` is chosen + * to avoid `-Wtype-limits` warnings from some compilers about unsigned comparison. + */ +#define mlib_upsize_integer(Value) \ + mlib_upsize_integer((uintmax_t)(intmax_t)((Value)), _mlibShouldTreatBitsAsSigned(Value)) +#define _mlibShouldTreatBitsAsSigned(Value) \ + /* NOLINTNEXTLINE(bugprone-sizeof-expression) */ \ + (sizeof ((Value)) < sizeof (intmax_t) || (_mlibGetOne(Value) - 2) < _mlibGetOne(Value)) +// Yield a 1 value of similar-ish type to the given expression. The ternary +// forces an integer promotion of literal 1 match the type of `V`, while leaving +// `V` unevaluated. Note that this will also promote `V` to be at least `(unsigned) int`, +// so the 1 value is only "similar" to `V`, and may be of a larger type +#define _mlibGetOne(V) (1 ? 1 : (V)) +// Function impl for upsize_integer +static inline mlib_upsized_integer +(mlib_upsize_integer) (uintmax_t bits, bool treat_as_signed) +{ + mlib_upsized_integer ret; + ret.bits.as_unsigned = bits; + ret.is_signed = treat_as_signed; + return ret; +} +// clang-format on + +#endif // MLIB_INTUTIL_H_INCLUDED diff --git a/bsonjs/common/mlib/loop.h b/bsonjs/common/mlib/loop.h new file mode 100644 index 0000000..1c4d0ef --- /dev/null +++ b/bsonjs/common/mlib/loop.h @@ -0,0 +1,165 @@ +/** + * @file mlib/loop.h + * @brief Looping utility macros + * @date 2025-01-29 + * + * @copyright Copyright (c) 2025 + * + * 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. + */ +#ifndef MLIB_LOOP_H_INCLUDED +#define MLIB_LOOP_H_INCLUDED + +#include + +#include +#include // u/intmax_t + +/** + * @brief Begin a loop over a range of integer values. Supports: + * + * - `mlib_foreach_{u,i}range(Var, Stop)` + * - `mlib_foreach_{u,i}range(Var, Start, Stop)` + * + * If omitted, starts at zero. The loop does not include the `Stop` value. The `Var` + * variable cannot be modified within the loop. The loop variable is declared as the maximum + * precision type for the requested signedness. + */ +#define mlib_foreach_urange(...) MLIB_ARGC_PICK(_mlib_foreach_urange, __VA_ARGS__) +#define mlib_foreach_irange(...) MLIB_ARGC_PICK(_mlib_foreach_irange, __VA_ARGS__) +#define _mlib_foreach_urange_argc_2(VarName, Stop) _mlib_foreach_urange_argc_3(VarName, 0, Stop) +#define _mlib_foreach_urange_argc_3(VarName, Start, Stop) \ + _mlibForeachRange(uintmax_t, \ + VarName, \ + Start, \ + Stop, \ + MLIB_PASTE(VarName, _start), \ + MLIB_PASTE(VarName, _stop), \ + MLIB_PASTE(VarName, _counter)) +#define _mlib_foreach_irange_argc_2(VarName, Stop) _mlib_foreach_irange_argc_3(VarName, 0, Stop) +#define _mlib_foreach_irange_argc_3(VarName, Start, Stop) \ + _mlibForeachRange(intmax_t, \ + VarName, \ + Start, \ + Stop, \ + MLIB_PASTE(VarName, _start), \ + MLIB_PASTE(VarName, _stop), \ + MLIB_PASTE(VarName, _counter)) + +/** + * @brief Loop over a pointed-to array + * + * @param T The type of the array elements + * @param Var Identifier to declare as the pointer to the current element + * @param ArrayPtr A pointer to the beginning of the array + * @param Count The number of elements in the array + */ +#define mlib_foreach(T, Var, ArrayPtr, Count) \ + _mlibForeach(T, Var, ArrayPtr, Count, MLIB_PASTE(Var, _start), MLIB_PASTE(Var, _stop), MLIB_PASTE(Var, _iter)) +/** + * @brief Loop over the elements of a C array + * + * @param T the type of the array elements + * @param Var Identifier to declare as the pointer to the current element + * @param Array An expression of array type (not a pointer) + */ +#define mlib_foreach_arr(T, Var, Array) mlib_foreach (T, Var, Array, (sizeof Array / sizeof Array[0])) + +// clang-format off +#define _mlibForeachRange(VarType, VarName, StartValue, StopValue, StartVar, StopVar, Counter) \ + _mlibLoopMagicBegin() \ + /* Capture the starting and stopping value first */ \ + for (VarType StartVar = (StartValue), StopVar = (StopValue); !_mlibLoopIsDone;) \ + _mlibLoopMagicEnd( \ + /* Init counter to the start value */ \ + VarType Counter = StartVar, \ + /* Stop when the counter is not less than the stop value */ \ + (_mlibLoopState.first = Counter == StartVar, \ + _mlibLoopState.last = Counter + 1 == StopVar, \ + Counter < StopVar), \ + /* Increment the counter at loop end */ \ + ++Counter, \ + /* Declare the loop variable as const at the start of each iteraiton */ \ + const VarType VarName = Counter) + +#define _mlibForeach(T, VarName, ArrayPtr, Count, StartVar, StopVar, Iter) \ + _mlibLoopMagicBegin() \ + /* Capture the starting and stopping position so we only evaluate them once */ \ + for (T* const StartVar = (ArrayPtr) + 0; !_mlibLoopIsDone;) \ + for (T* const StopVar = StartVar + (Count); !_mlibLoopIsDone;) \ + _mlibLoopMagicEnd( \ + /* Init the iteration pointer to the array start */ \ + T* Iter = StartVar, \ + /* Stop when the iterator points to the stop position */ \ + (_mlibLoopState.first = Iter == StartVar, \ + _mlibLoopState.last = Iter + 1 == StopVar, \ + Iter != StopVar), \ + /* Advance the iterator on each loop */ \ + ++Iter, \ + /* Declare a constant pointer to the current element at the top of the loop */ \ + T* const VarName = Iter) + + +#define _mlibLoopDidBreak MLIB_PASTE(_mlibLoopDidBreak_lno_, __LINE__) +#define _mlibLoopOnce MLIB_PASTE(_mlibLoopOnce_lno_, __LINE__) +#define _mlibLoopIsDone MLIB_PASTE(_mlibLoopIsDone_lno_, __LINE__) +#define _mlibLoopIsState MLIB_PASTE(_mlibLoopIsState_lno_, __LINE__) +#define _mlibLoopMagicBegin() \ + /* Loop stop condition */ \ + for (int _mlibLoopIsDone = 0; !_mlibLoopIsDone;) \ + /* Track if the user broke out of the inner loop */ \ + for (int _mlibLoopDidBreak = 0; !_mlibLoopIsDone;) \ + /* Loop variables */ \ + for (struct mlib_loop_state _mlibLoopState = {0, 0, 0}; !_mlibLoopIsDone;) + +/** + * @brief Struct type declared within the scope of an `mlib_foreach` loop, which + * contains information about the running loop. + */ +struct mlib_loop_state { + // The current zero-based index of the loop + size_t index; + // Whether the current iteration is the first in the loop + bool first; + // Whether the current iteration will be the last in the loop + bool last; +}; + +/// InitStmt: Statement that executes once at the top of the loop +/// ContinueCond: Condition at which the loop will stop +/// StepExpr: Expression for the loop step +/// HeadStmt: A statement that appears at the head of the loop, executed once on each iteration +#define _mlibLoopMagicEnd(InitStmt, ContinueCond, StepExpr, HeadStmt) \ + for (\ + /* Run the init statement */ \ + InitStmt; \ + /* Test the loop condition, unless we `break` out of the loop */ \ + !(_mlibLoopIsDone = _mlibLoopIsDone || !(ContinueCond)); \ + /* Run the step expression, unless we `break` from the loop */ \ + (void)(_mlibLoopIsDone || ((void)(StepExpr), 1))) \ + /* `break` detection: */ \ + for (int _mlibLoopOnce = 0; !_mlibLoopOnce; _mlibLoopOnce = 1, _mlibLoopIsDone = _mlibLoopDidBreak) \ + /* Loop state information */ \ + for (const struct mlib_loop_state loop = _mlibLoopState; \ + !_mlibLoopOnce; \ + ((void)loop, _mlibLoopOnce = 1, ++_mlibLoopState.index)) \ + for (HeadStmt; \ + /* Set `_mlibLoopDidBreak` to true at the start of the loop: */ \ + !_mlibLoopOnce && (_mlibLoopDidBreak = 1); \ + /* If loop exits normally, set `_mlibLoopDidBreak` to false */ \ + _mlibLoopDidBreak = 0, _mlibLoopOnce = 1) + + +// clang-format on + +#endif // MLIB_LOOP_H_INCLUDED diff --git a/bsonjs/common/mlib/platform.h b/bsonjs/common/mlib/platform.h new file mode 100644 index 0000000..8809575 --- /dev/null +++ b/bsonjs/common/mlib/platform.h @@ -0,0 +1,53 @@ +/** + * @file mlib/platform.h + * @brief Operating System Headers and Definitions + * @date 2025-04-21 + * + * This file will conditionally include the general system headers available + * for the current host platform. + * + * @copyright Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MLIB_PLATFORM_H_INCLUDED +#define MLIB_PLATFORM_H_INCLUDED + +// clang-format off + +// Windows headers +#ifdef _WIN32 + // Check that our WINNT version isn't too old to be used + #if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x601) + #undef _WIN32_WINNT + #endif + #ifndef _WIN32_WINNT + // Request a new-enough version of the Win32 API (required for MinGW) + #define _WIN32_WINNT 0x601 + #endif + // Winsock must be included before windows.h + #include // IWYU pragma: export + #include // IWYU pragma: export +#endif + +// POSIX headers +#if defined(__unix__) || defined(__unix) || defined(__APPLE__) + #include // IWYU pragma: export + #include // IWYU pragma: export + #include // IWYU pragma: export +#endif + +// clang-format on + +#endif // MLIB_PLATFORM_H_INCLUDED diff --git a/bsonjs/common/mlib/str.h b/bsonjs/common/mlib/str.h new file mode 100644 index 0000000..795ad7c --- /dev/null +++ b/bsonjs/common/mlib/str.h @@ -0,0 +1,1239 @@ +/** + * @file mlib/str.h + * @brief String handling utilities + * @date 2025-04-30 + * + * This file provides utilities for handling *sized* strings. That is, strings + * that carry their size, and do not rely on null termination. These APIs also + * do a lot more bounds checking than is found in ``. + * + * @copyright Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MLIB_STR_H_INCLUDED +#define MLIB_STR_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +#include // va_list +#include +#include +#include +#include // vsnprintf +#include // malloc/free +#include // memcpy + +/** + * @brief A simple non-owning string-view type. + * + * The viewed string can be treated as an array of `char`. It's pointed-to data + * must not be freed or manipulated. + * + * @note The viewed string is NOT guaranteed to be null-terminated. It WILL + * be null-terminated if: Directly created from a string literal, a C string, or + * a null-terminated `mstr_view`. + * @note The viewed string MAY contain nul (zero-value) characters, so using them + * with C string APIs could truncate unexpectedly. + * @note The view itself may be "null" if the `data` member of the string view + * is a null pointer. A zero-initialized `mstr_view` is null. + */ +typedef struct mstr_view { + /** + * @brief Pointer to the string data viewed by this object. + * + * - This pointer may be null, in which case the string view itself is "null". + * - If `len > 1`, then this points to a contiguous array of `char` of length + * `len`. + * - If `len == 1`, then this *may* point to a single `char` object. + * - The pointed-to string might not be a null-terminated C string. Accessing + * the `char` value at `data[len]` is undefined behavior. + */ + const char *data; + /** + * @brief The length of the viewed string pointed-to by `data` + * + * If `data` points to a single `char` object, then this must be `1`. If + * `data` is a null pointer, then this value should be zero. + */ + size_t len; +} mstr_view; + +/** + * @brief Expand to the two printf format arguments required to format an mstr object + * + * You should use the format specifier `%.*s' for all mstr strings. + * + * This is just a convenience shorthand. + */ +#define MSTR_FMT(S) (int)mstr_view_from(S).len, mstr_view_from(S).data + +/** + * @brief Create an `mstr_view` that views the given array of `char` + * + * @param data Pointer to the beginning of the string, or pointer to a single + * `char`, or a null pointer + * @param len Length of the new string-view. If `data` points to a single `char`, + * this must be `0` or `1`. If `data` is a null pointer, this should be `0`. + * + * @note This is defined as a macro that expands to a compound literal to prevent + * proliferation of redundant function calls in debug builds. + */ +#define mstr_view_data(DataPointer, Length) (mlib_init(mstr_view){(DataPointer), (Length)}) + +#if 1 // See "!! NOTE" below + +/** + * @brief Coerce a string-like object to an `mstr_view` of that string + * + * This macro requires that the object have `.data` and `.len` members. + * + * @note This macro will double-evaluate its argument. + */ +#define mstr_view_from(X) mstr_view_data((X).data, (X).len) + +/** + * ! NOTE: The disabled snippet below is kept for posterity as a drop-in replacment + * ! for mstr_view_from with support for _Generic. + * + * When we can increase the compiler requirements to support _Generic, the following + * macro definition alone makes almost every function in this file significantly + * more concise to use, as it allows us to pass a C string to any API that + * expects an `mstr_view`, enabling code like this: + * + * ``` + * mstr s = get_string(); + * if (mstr_cmp(s, ==, "magicKeyword")) { + * Do something... + * } + * ``` + * + * This also allows us to avoid the double-evaluation problem presented by + * `mstr_view_from` being defined as above. + * + * Without _Generic, we require all C strings to be wrapped with `mstr_cstring`, + * which isn't especially onerous, but it is annoying. Additionally, the below + * `_Generic` macro can be extended to support more complex string-like types. + * + * For reference, support for _Generic requires the following compilers: + * + * - MSVC 19.28.0+ (VS 2019, 16.8.1) + * - GCC 4.9+ + * - Clang 3.0+ + */ + +#else + +/** + * @brief Coerce an object to an `mstr_view` + * + * The object requires a `data` and `len` member + */ +#define mstr_view_from(X) \ + _Generic((X), mstr_view: _mstr_view_trivial_copy, char *: mstr_cstring, const char *: mstr_cstring)((X)) +// Just copy an mstr_view by-value +static inline mstr_view +_mstr_view_trivial_copy(mstr_view s) +{ + return s; +} + +#endif + + +/** + * @brief Create an `mstr_view` referring to the given null-terminated C string + * + * @param s Pointer to a C string. The length of the returned string is infered using `strlen` + * + * This should not defined as a macro, because defining it as a macro would require + * double-evaluating for the call to `strlen`. + */ +static inline mstr_view +mstr_cstring(const char *s) +{ + const size_t l = strlen(s); + return mstr_view_data(s, l); +} + +/** + * @brief Compare two strings lexicographically by each code unit + * + * If called with two arguments behaves the same as `strcmp`. If called with + * three arguments, the center argument should be an infix operator to perform + * the semantic comparison. + */ +static inline enum mlib_cmp_result +mstr_cmp(mstr_view a, mstr_view b) +{ + size_t l = a.len; + if (b.len < l) { + l = b.len; + } + // Use `memcmp`, not `strncmp`: We want to respect nul characters + int r = memcmp(a.data, b.data, l); + if (r) { + // Not equal: Compare with zero to normalize to the cmp_result value + return mlib_cmp(r, 0); + } + // Same prefixes, the ordering is now based on their length (longer string > shorter string) + return mlib_cmp(a.len, b.len); +} + +#define mstr_cmp(...) MLIB_ARGC_PICK(_mstr_cmp, __VA_ARGS__) +#define _mstr_cmp_argc_2(A, B) mstr_cmp(mstr_view_from(A), mstr_view_from(B)) +#define _mstr_cmp_argc_3(A, Op, B) (_mstr_cmp_argc_2(A, B) Op 0) + +/** + * @brief If the given codepoint is a Basic Latin (ASCII) uppercase character, + * return the lowercase character. Other codepoint values are returned unchanged. + * + * This is safer than `tolower`, because it doesn't respect locale and has no + * undefined behavior. + */ +static inline int32_t +mlib_latin_tolower(int32_t a) +{ + if (a >= 0x41 /* "A" */ && a <= 0x5a /* "Z" */) { + a += 0x20; // Adjust from "A" -> "a" + } + return a; +} + +/** + * @brief Compare two individual codepoint values, with case-insensitivity in + * the Basic Latin range. + */ +static inline enum mlib_cmp_result +mlib_latin_charcasecmp(int32_t a, int32_t b) +{ + return mlib_cmp(mlib_latin_tolower(a), mlib_latin_tolower(b)); +} + +/** + * @brief Compare two strings lexicographically, case-insensitive in the Basic + * Latin range. + * + * If called with two arguments, behaves the same as `strcasecmp`. If called with + * three arguments, the center argument should be an infix operator to perform + * the semantic comparison. + */ +static inline enum mlib_cmp_result +mstr_latin_casecmp(mstr_view a, mstr_view b) +{ + size_t l = a.len; + if (b.len < l) { + l = b.len; + } + mlib_foreach_urange (i, l) { + // We don't need to do any UTF-8 decoding, because our case insensitivity + // only activates for 1-byte encoded codepoints, and all other valid UTF-8 + // sequences will collate equivalently with byte-wise comparison to a UTF-32 + // encoding. + enum mlib_cmp_result r = mlib_latin_charcasecmp(a.data[i], b.data[i]); + if (r) { + // Not equivalent at this code unit. Return this as the overall string ordering. + return r; + } + } + // Same prefixes, the ordering is now based on their length (longer string > shorter string) + return mlib_cmp(a.len, b.len); +} + +#define mstr_latin_casecmp(...) MLIB_ARGC_PICK(_mstr_latin_casecmp, __VA_ARGS__) +#define _mstr_latin_casecmp_argc_2(A, B) mstr_latin_casecmp(mstr_view_from(A), mstr_view_from(B)) +#define _mstr_latin_casecmp_argc_3(A, Op, B) (_mstr_latin_casecmp_argc_2(A, B) Op 0) + +/** + * @brief Adjust a possibly negative index position to wrap around for a string + * + * @param s The string to be respected for index wrapping + * @param pos The maybe-negative index to be adjusted + * @param clamp_to_length If `true` and given a non-negative value, if that + * value is greater than the string length, this function will return the string + * length instead. + * @return size_t The new zero-based non-negative index + * + * If `pos` is negative, then it represents indexing from the end of the string, + * where `-1` refers to the last character in the string, `-2` the penultimate, + * etc. If the absolute value is greater than the length of the string, the + * program will be terminated. + */ +static inline size_t +_mstr_adjust_index(mstr_view s, mlib_upsized_integer pos, bool clamp_to_length) +{ + if (clamp_to_length && (mlib_cmp)(pos, mlib_upsize_integer(s.len), 0) == mlib_greater) { + // We want to clamp to the length, and the given value is greater than the string length. + return s.len; + } + if (pos.is_signed && pos.bits.as_signed < 0) { + // This will add the negative value to the length of the string. If such + // an operation would result a negative value, this will terminate the + // program. + return mlib_assert_add(size_t, s.len, pos.bits.as_signed); + } + // No special behavior, just assert that the given position is in-bounds for the string + mlib_check( + pos.bits.as_unsigned <= s.len, because, "the string position index must not be larger than the string length"); + return pos.bits.as_unsigned; +} + +/** + * @brief Obtain the code unit at the given zero-based index, with negative index wrapping. + * + * This function asserts that the index is in-bounds for the given string. + * + * @param s The string to be inspected. + * @param pos The index to access. Zero is the first code unit, and -1 is the last. + * @return char The code unit at position `pos`. + */ +static inline char +mstr_at(mstr_view s, mlib_upsized_integer pos_) +{ + size_t pos = _mstr_adjust_index(s, pos_, false); + return s.data[pos]; +} + +#define mstr_at(S, Pos) (mstr_at)(mstr_view_from(S), mlib_upsize_integer(Pos)) + +/** + * @brief Create a new `mstr_view` that views a substring within another string + * + * @param s The original string view to be inspected + * @param pos The number of `char` to skip in `s`, or a negative value to + * pos from the end of the string. + * @param len The length of the new string view (optional, default SIZE_MAX) + * + * The length of the string view is clamped to the characters available in `s`, + * so passing a too-large value for `len` is well-defined. Passing a too-large + * value for `pos` will abort the program. + * + * Callable as: + * + * - `mstr_substr(s, pos)` + * - `mstr_substr(s, pos, len)` + */ +static inline mstr_view +mstr_substr(mstr_view s, mlib_upsized_integer pos_, size_t len) +{ + const size_t pos = _mstr_adjust_index(s, pos_, false); + // Number of characters in the string after we remove the prefix + const size_t remain = s.len - pos; + // Clamp the new length to the size that is actually available. + if (len > remain) { + len = remain; + } + return mstr_view_data(s.data + pos, len); +} + +#define mstr_substr(...) MLIB_ARGC_PICK(_mstr_substr, __VA_ARGS__) +#define _mstr_substr_argc_2(Str, Start) _mstr_substr_argc_3(Str, Start, SIZE_MAX) +#define _mstr_substr_argc_3(Str, Start, Stop) mstr_substr(mstr_view_from(Str), mlib_upsize_integer(Start), Stop) + +/** + * @brief Obtain a slice of the given string view, where the two arguments are zero-based indices into the string + * + * @param s The string to be sliced + * @param start The zero-based index of the new string start + * @param end The zero-based index of the first character to exclude from the new string + * + * @note Unlike `substr`, the second argument is required, and must specify the index at which the + * string will end, rather than the length of the string. + */ +static inline mstr_view +mstr_slice(const mstr_view s, const mlib_upsized_integer start_, const mlib_upsized_integer end_) +{ + const size_t start_pos = _mstr_adjust_index(s, start_, false); + const size_t end_pos = _mstr_adjust_index(s, end_, true); + mlib_check(end_pos >= start_pos, because, "Slice positions must end after the start position"); + const size_t sz = (size_t)(end_pos - start_pos); + return mstr_substr(s, start_pos, sz); +} +#define mstr_slice(S, StartPos, EndPos) \ + mstr_slice(mstr_view_from(S), mlib_upsize_integer((StartPos)), mlib_upsize_integer((EndPos))) + +/** + * @brief Find the first occurrence of `needle` within `hay`, returning the zero-based index + * if found, and `SIZE_MAX` if it is not found. + * + * @param hay The string which is being scanned + * @param needle The substring that we are searching to find + * @param pos The start position of the search (optional, default zero) + * @param len The number of characters to search in `hay` (optional, default SIZE_MAX) + * @return size_t If found, the zero-based index of the first occurrence within + * the string. If not found, returns `SIZE_MAX`. + * + * The `len` is clamped to the available string length. + * + * Callable as: + * + * - `mstr_find(hay, needle)` + * - `mstr_find(hay, needle, pos)` + * - `mstr_find(hay, needle, pos, len)` + */ +static inline size_t +mstr_find(mstr_view hay, mstr_view const needle, mlib_upsized_integer const pos_, size_t const len) +{ + const size_t pos = _mstr_adjust_index(hay, pos_, false); + // Trim the hay according to our search window: + hay = mstr_substr(hay, pos, len); + + // Larger needle can never exist within the smaller string: + if (hay.len < needle.len) { + return SIZE_MAX; + } + + // Set the index at which we can stop searching early. This will never + // overflow, because we guard against hay.len > needle.len + size_t stop_idx = hay.len - needle.len; + // Use "<=", because we do want to include the final search position + for (size_t offset = 0; offset <= stop_idx; ++offset) { + if (memcmp(hay.data + offset, needle.data, needle.len) == 0) { + // Return the found position. Adjust by the start pos since we may + // have trimmed the search window + return offset + pos; + } + } + + // Nothing was found. Return SIZE_MAX to indicate the not-found + return SIZE_MAX; +} + +#define mstr_find(...) MLIB_ARGC_PICK(_mstr_find, __VA_ARGS__) +#define _mstr_find_argc_2(Hay, Needle) _mstr_find_argc_3(Hay, Needle, 0) +#define _mstr_find_argc_3(Hay, Needle, Start) _mstr_find_argc_4(Hay, Needle, Start, SIZE_MAX) +#define _mstr_find_argc_4(Hay, Needle, Start, Stop) \ + mstr_find(mstr_view_from(Hay), mstr_view_from(Needle), mlib_upsize_integer(Start), Stop) + +/** + * @brief Find the zero-based index of the first `char` in `hay` that also occurs in `needles` + * + * This is different from `find()` because it considers each char in `needles` as an individual + * one-character string to be search for in `hay`. + * + * @param hay The string to be searched + * @param needles A string containing a set of characters which are searched for in `hay` + * @param pos The index at which to begin searching (optional, default is zero) + * @param len The number of characters in `hay` to consider before stopping (optional, default is SIZE_MAX) + * @return size_t If a needle is found, returns the zero-based index of that first needle. + * Otherwise, returns SIZE_MAX. + * + * Callable as: + * + * - `mstr_find_first_of(hay, needles)` + * - `mstr_find_first_of(hay, needles, pos)` + * - `mstr_find_first_of(hay, needles, pos, len)` + */ +static inline size_t +mstr_find_first_of(mstr_view hay, mstr_view const needles, mlib_upsized_integer const pos_, size_t const len) +{ + const size_t pos = _mstr_adjust_index(hay, pos_, false); + // Trim to fit the search window + hay = mstr_substr(hay, pos, len); + // We search by incrementing an index + mlib_foreach_urange (idx, hay.len) { + // Grab a substring of the single char at the current search index + mstr_view one = mstr_substr(hay, idx, 1); + // Test if the single char occurs anywhere in the needle set + if (mstr_find(needles, one) != SIZE_MAX) { + // We found the first index in `hay` where one of the needles occurs. Adjust + // by `pos` since we may have trimmed + return idx + pos; + } + } + return SIZE_MAX; +} + +#define mstr_find_first_of(...) MLIB_ARGC_PICK(_mstr_find_first_of, __VA_ARGS__) +#define _mstr_find_first_of_argc_2(Hay, Needle) _mstr_find_first_of_argc_3(Hay, Needle, 0) +#define _mstr_find_first_of_argc_3(Hay, Needle, Pos) _mstr_find_first_of_argc_4(Hay, Needle, Pos, SIZE_MAX) +#define _mstr_find_first_of_argc_4(Hay, Needle, Pos, Len) mstr_find_first_of(Hay, Needle, mlib_upsize_integer(Pos), Len) + +/** + * @brief Test whether the given codepoint is a Basic Latin whitespace character + * + * This function does not depend on the locale and has no undefined behavior, unlike functions + * + * @param c The codepoint to be tested + */ +static inline bool +mlib_is_latin_whitespace(int32_t c) +{ + switch (c) { + case 0x09: // horizontal tab + case 0x0a: // line feed + case 0x0d: // carriage return + case 0x20: // space + return true; + + default: + return false; + } +} + +/** + * @brief Trim leading latin (ASCII) whitespace from the given string + * + * @param s The string to be inspected + * @return mstr_view A substring view of `s` that excludes any leading whitespace + */ +static inline mstr_view +mstr_trim_left(mstr_view s) +{ + // Testing arbitrary code units for whitespace is safe as only 1-byte-encoded + // codepoints can land within the Basic Latin range: + while (s.len && mlib_is_latin_whitespace(mstr_at(s, 0))) { + s = mstr_substr(s, 1); + } + return s; +} +#define mstr_trim_left(S) (mstr_trim_left)(mstr_view_from(S)) + +/** + * @brief Trim trailing latin (ASCII) whitespace from the given string + * + * @param s The string to be insepcted + * @return mstr_view A substring view of `s` that excludes any trailing whitespace. + */ +static inline mstr_view +mstr_trim_right(mstr_view s) +{ + while (s.len && mlib_is_latin_whitespace(mstr_at(s, -1))) { + s = mstr_slice(s, 0, -1); + } + return s; +} +#define mstr_trim_right(S) (mstr_trim_right)(mstr_view_from(S)) + +/** + * @brief Trim leading and trailing latin (ASCII) whitespace from the string + * + * @param s The string to be inspected + * @return mstr_view A substring of `s` that excludes leading and trailing whitespace. + */ +static inline mstr_view +mstr_trim(mstr_view s) +{ + s = mstr_trim_left(s); + s = mstr_trim_right(s); + return s; +} +#define mstr_trim(S) (mstr_trim)(mstr_view_from(S)) + +/** + * @brief Split a single string view into two strings at the given position + * + * @param s The string to be split + * @param pos The position at which the prefix string is ended + * @param drop [optional] The number of characters to drop between the prefix and suffix + * @param prefix [out] Updated to point to the part of the string before the split + * @param suffix [out] Updated to point to the part of the string after the split + * + * `pos` and `drop` are clamped to the size of the input string. + * + * Callable as: + * + * - `mstr_split_at(s, pos, prefix, suffix)` + * - `mstr_split_at(s, pos, drop, prefix, suffix)` + * + * If either `prefix` or `suffix` is a null pointer, then they will be ignored + */ +static inline void +mstr_split_at(mstr_view s, mlib_upsized_integer pos_, size_t drop, mstr_view *prefix, mstr_view *suffix) +{ + const size_t pos = _mstr_adjust_index(s, pos_, true /* clamp to the string size */); + // Save the prefix string + if (prefix) { + *prefix = mstr_substr(s, 0, pos); + } + // Save the suffix string + if (suffix) { + // The number of characters that remain after the prefix is removed + const size_t remain = s.len - pos; + // Clamp the number of chars to drop to not overrun the input string + if (remain < drop) { + drop = remain; + } + // The start position of the new string + const size_t next_start = pos + drop; + *suffix = mstr_substr(s, next_start, SIZE_MAX); + } +} + +#define mstr_split_at(...) MLIB_ARGC_PICK(_mstr_split_at, __VA_ARGS__) +#define _mstr_split_at_argc_4(Str, Pos, Prefix, Suffix) _mstr_split_at_argc_5(Str, Pos, 0, Prefix, Suffix) +#define _mstr_split_at_argc_5(Str, Pos, Drop, Prefix, Suffix) \ + mstr_split_at(mstr_view_from(Str), mlib_upsize_integer(Pos), Drop, Prefix, Suffix) + +/** + * @brief Split a string in two around the first occurrence of some infix string. + * + * @param s The string to be split in twain + * @param infix The infix string to be searched for + * @param prefix The part of the string that precedes the infix (nullable) + * @param suffix The part of the string that follows the infix (nullable) + * @return true If the infix was found + * @return false Otherwise + * + * @note If `infix` does not occur in `s`, then `*prefix` will be set equal to `s`, + * and `*suffix` will be made an empty string, as if the infix occurred at the end + * of the string. + */ +static inline bool +mstr_split_around(mstr_view s, mstr_view infix, mstr_view *prefix, mstr_view *suffix) +{ + // Find the position of the infix. If it is not found, returns SIZE_MAX + const size_t pos = mstr_find(s, infix); + // Split at the infix, dropping as many characters as are in the infix. If + // the `pos` is SIZE_MAX, then this call will clamp to the end of the string. + mstr_split_at(s, pos, infix.len, prefix, suffix); + // Return `true` if we found the infix, indicated by a not-SIZE_MAX `pos` + return pos != SIZE_MAX; +} + +#define mstr_split_around(Str, Infix, PrefixPtr, SuffixPtr) \ + mstr_split_around(mstr_view_from((Str)), mstr_view_from((Infix)), (PrefixPtr), (SuffixPtr)) + +/** + * @brief Test whether the given string starts with the given prefix + * + * @param str The string to be tested + * @param prefix The prefix to be searched for + * @return true if-and-only-if `str` starts with `prefix` + * @return false Otherwise + */ +static inline bool +mstr_starts_with(mstr_view str, mstr_view prefix) +{ + // Trim to match the length of the prefix we want + str = mstr_substr(str, 0, prefix.len); + // Check if the trimmed string is the same as the prefix + return mstr_cmp(str, ==, prefix); +} +#define mstr_starts_with(Str, Prefix) mstr_starts_with(mstr_view_from(Str), mstr_view_from(Prefix)) + +/** + * @brief Test whether a substring occurs at any point within the given string + * + * @param str The string to be inspected + * @param needle The substring to be searched for + * @return true If-and-only-if `str` contains `needle` at any position + * @return false Otherise + */ +static inline bool +mstr_contains(mstr_view str, mstr_view needle) +{ + return mstr_find(str, needle) != SIZE_MAX; +} +#define mstr_contains(Str, Needle) mstr_contains(mstr_view_from(Str), mstr_view_from(Needle)) + +/** + * @brief Test whether a given string contains any of the characters in some other string + * + * @param str The string to be inspected + * @param needle A string to be treated as a set of one-byte characters to search for + * @return true If-and-only-if `str` contains `needle` at any position + * @return false Otherise + * + * @note This function does not currently support multi-byte codepoints + */ +static inline bool +mstr_contains_any_of(mstr_view str, mstr_view needle) +{ + return mstr_find_first_of(str, needle) != SIZE_MAX; +} +#define mstr_contains_any_of(Str, Needle) mstr_contains_any_of(mstr_view_from(Str), mstr_view_from(Needle)) + + +/** + * @brief A simple mutable string type, with a guaranteed null terminator. + * + * This type is a trivially relocatable aggregate type that contains a pointer `data` + * and a size `len`. If not null, the pointer `data` points to an array of mutable + * `char` of length `len + 1`, where the character at `data[len]` is always zero, + * and must not be modified. + * + * @note The string MAY contain nul (zero-value) characters, so using them with + * C string APIs could truncate unexpectedly. + * @note The string itself may be "null" if the `data` member of the string is + * a null pointer. A zero-initialized `mstr` is null. The null string is distinct + * from the empty string, which has a non-null `.data` that points to an empty + * C string. + */ +typedef struct mstr { + /** + * @brief Pointer to the first char in the string, or NULL if + * the string is null. + * + * The pointed-to character array has a length of `len + 1`, where + * the character at `data[len]` is always null. + * + * @warning Attempting to overwrite the null character at `data[len]` + * will result in undefined behavior! + * + * @note An empty string is not equivalent to a null string! An empty string + * will still point to an array of length 1, where the only char is the null + * terminator. + */ + char *data; + /** + * @brief The number of characters in the array pointed-to by `data` + * that precede the null terminator. + */ + size_t len; +} mstr; + + +/** + * @brief Resize an existing or null `mstr`, without initializing any of the + * added content other than the null terminator. This operation is potentially + * UNSAFE, because it gives uninitialized memory to the caller. + * + * @param str Pointer to a valid `mstr`, or a null `mstr`. + * @param new_len The new length of the string. + * @return true If the operation succeeds + * @return false Otherwise + * + * If `str` is a null string, this function will initialize a new `mstr` object + * on-the-fly. + * + * If the operation increases the length of the string (or initializes a new string), + * then the new `char` in `str.data[str.len : new_len] will contain uninitialized + * values. The char at `str.data[new_len]` WILL be set to zero, to ensure there + * is a null terminator. The caller should always initialize the new string + * content to ensure that the string has a specified value. + */ +static inline bool +mstr_resize_for_overwrite(mstr *const str, const size_t new_len) +{ + // We need to allocate one additional char to hold the null terminator + size_t alloc_size = new_len; + if (mlib_unlikely(mlib_add(&alloc_size, 1) || alloc_size > PTRDIFF_MAX)) { + // Allocation size is too large + return false; + } + // Try to (re)allocate the region + char *data = (char *)realloc(str->data, alloc_size); + if (!data) { + // Failed to (re)allocate + return false; + } + // Note: We do not initialize any of the data in the newly allocated region. + // We only set the null terminator. It is up to the caller to do the rest of + // the init. + data[new_len] = '\0'; + // Update the final object + str->data = data; + str->len = new_len; + // Success + return true; +} + +/** + * @brief Given an existing `mstr`, resize it to hold `new_len` chars + * + * @param str Pointer to a string object to update, or a null `mstr` + * @param new_len The new length of the string, not including the implicit null terminator + * @return true If the operation succeeds + * @return false Otherwise + * + * @note If the operation fails, then `*str` is not modified. + */ +static inline bool +mstr_resize(mstr *str, size_t new_len) +{ + const size_t old_len = str->len; + if (!mstr_resize_for_overwrite(str, new_len)) { + // Failed to allocate new storage for the string + return false; + } + // Check how many chars we added/removed + const ptrdiff_t len_diff = mlib_assert_sub(ptrdiff_t, new_len, str->len); + if (len_diff > 0) { + // We added new chars. Zero-init all the new chars + memset(str->data + old_len, 0, (size_t)len_diff); + } + // Success + return true; +} + +/** + * @brief Create a new `mstr` of the given length + * + * @param new_len The length of the new string, in characters, not including the null terminator + * @return mstr A new string. The string's `data` member is NULL in case of failure + * + * The character array allocated for the string will always be `new_len + 1` `char` in length, + * where the char at the index `new_len` is a null terminator. This means that a string of + * length zero will allocate a single character to store the null terminator. + * + * All characters in the new string are initialize to zero. If you want uninitialized + * string content, use `mstr_resize_for_overwrite`. + */ +static inline mstr +mstr_new(size_t new_len) +{ + mstr ret = {NULL, 0}; + // We can rely on `resize` to handle the null state properly. + mstr_resize(&ret, new_len); + return ret; +} + +/** + * @brief Free the resources associated with an mstr object. + * + * @param s Pointer to an `mstr` object. If pointer or the pointed-to-object is null, + * this function is a no-op. + * + * After this call, the pointed-to `s` will be a null `mstr` + */ +static inline void +mstr_destroy(mstr *s) +{ + if (s) { + free(s->data); + s->len = 0; + s->data = NULL; + } +} + +/** + * @brief Obtain a null mstr string object. + * + * @return mstr A null string, with a null data pointer and zero size + */ +static inline mstr +mstr_null(void) +{ + return mlib_init(mstr){0}; +} + +/** + * @internal + * @brief Test whether the given string-view is a view within the given owning string + */ +static inline bool +_mstr_overlaps(mstr const *str, mstr_view sv) +{ + // Note: Pointer-comparison between objects is unspecified, but is guaranteed + // to returns `true` if there is overlap. We're okay with false-positive overlaps. + // Additionally, POSIX and Win32 both offer stronger guarantees about pointer + // comparison, which we can rely on here. + return str->data // + && str->data <= sv.data // + && sv.data <= str->data + str->len; +} + +/** + * @brief Replace the content of the given string, attempting to reuse the buffer + * + * @param inout Pointer to a valid or null `mstr` to be replaced + * @param s The new string contents + * @return true If the operation succeeded + * @return false Otherwise + * + * If the operation fails, `*inout` is not modified + */ +static inline bool +mstr_assign(mstr *inout, mstr_view s) +{ + // Check for self-assignment + if (_mstr_overlaps(inout, s)) { + // We are overwriting a string with a (sub)string of its own content. + // Move the substring to the front of the string (may be a no-op if `s` + // points to the beginning of the string) + memmove(inout->data, s.data, s.len); + // Resize to truncate. This will always shrink the string, because a valid + // string-view into `inout` cannot be longer than `inout` itself. Thus, it + // also cannot fail. + mstr_resize_for_overwrite(inout, s.len); + return true; + } + if (!mstr_resize_for_overwrite(inout, s.len)) { + return false; + } + memcpy(inout->data, s.data, s.len); + return true; +} + +#define mstr_assign(InOut, S) mstr_assign((InOut), mstr_view_from((S))) + +/** + * @brief Create a mutable copy of the given string. + * + * @param sv The string to be copied + * @return mstr A new valid string, or a null string in case of allocation failure. + */ +static inline mstr +mstr_copy(mstr_view sv) +{ + mstr ret = {NULL, 0}; + mstr_assign(&ret, sv); + return ret; +} + +#define mstr_copy(S) mstr_copy(mstr_view_from((S))) +#define mstr_copy_cstring(S) mstr_copy(mstr_cstring((S))) + +/** + * @brief Concatenate two strings into a new mutable string + * + * @param a The left-hand string to be concatenated + * @param b The right-hand string to be concatenated + * @return mstr A new valid string composed by concatenating `a` with `b`, or + * a null string in case of allocation failure. + */ +static inline mstr +mstr_concat(mstr_view a, mstr_view b) +{ + mstr ret = {NULL, 0}; + size_t cat_len = 0; + if (mlib_unlikely(mlib_add(&cat_len, a.len, b.len))) { + // Size would overflow. No go. + return ret; + } + // Prepare the new string + if (!mstr_resize_for_overwrite(&ret, cat_len)) { + // Failed to allocate. The ret string is still null, and we can just return it + return ret; + } + // Copy in the characters from `a` + char *out = ret.data; + memcpy(out, a.data, a.len); + // Copy in the characters from `b` + out += a.len; + memcpy(out, b.data, b.len); + // Success + return ret; +} + +#define mstr_concat(A, B) mstr_concat(mstr_view_from((A)), mstr_view_from((B))) + +/** + * @brief Delete and/or insert characters into a string + * + * @param str The string object to be updated + * @param splice_pos The position at which to do the splice + * @param n_delete The number of characters to delete at `splice_pos` + * @param insert A string to be inserted at `split_pos` after chars are deleted + * @return true If the operation succeeds + * @return false Otherwise + * + * If `n_delete` is zero, then no characters are deleted. If `insert` is empty + * or null, then no characters are inserted. + */ +static inline bool +mstr_splice(mstr *str, size_t splice_pos, size_t n_delete, mstr_view insert) +{ + // Guard against self-insertion: + if (insert.data && _mstr_overlaps(str, insert)) { + // The insertion string exists within the current string. We cannot modify it in-place. + // Duplicate the insertion string to remain pristine while we splice: + mstr insert_dup = mstr_copy(insert); + if (!insert_dup.data) { + // Failed to dup the insert string. Failure to splice + return false; + } + // Do the splice, now using the copy of the insertion string + const bool ok = mstr_splice(str, splice_pos, n_delete, mstr_view_from(insert_dup)); + // We're done with the dup + mstr_destroy(&insert_dup); + // Return the sub-result + return ok; + } + mlib_check(splice_pos <= str->len); + // How many chars is it possible to delete from `splice_pos`? + size_t n_chars_avail_to_delete = str->len - splice_pos; + // Clamp to the number of chars available for deletion: + if (n_delete > n_chars_avail_to_delete) { + n_delete = n_chars_avail_to_delete; + } + // Compute the new string length + size_t new_len = str->len; + // This should never fail, because we should never try to delete more chars than we have + mlib_check(!mlib_sub(&new_len, n_delete)); + // Check if appending would make too big of a string + if (mlib_unlikely(mlib_add(&new_len, insert.len))) { + // New string will be too long + return false; + } + char *mut = str->data; + // We either resize first or resize last, depending on where we are shifting chars + if (new_len > str->len) { + // Do the resize first + if (!mstr_resize_for_overwrite(str, new_len)) { + // Failed to allocate + return false; + } + mut = str->data; + } + // Move to the splice position + mut += splice_pos; + // Shift the existing string parts around for the deletion operation + const size_t tail_len = n_chars_avail_to_delete - n_delete; + // Adjust to the begining of the string part that we want to keep + char *copy_from = mut + n_delete; + char *copy_to = mut + insert.len; + memmove(copy_to, copy_from, tail_len); + if (new_len < str->len) { + // We didn't resize first, so resize now. We are shrinking the string, so this + // will never fail, and does not create any uninitialized memory: + mlib_check(mstr_resize_for_overwrite(str, new_len)); + mut = str->data + splice_pos; + } + // Insert the new data if the insertion string is non-null + if (insert.data) { + memcpy(mut, insert.data, insert.len); + } + return true; +} + +/** + * @brief Append a string to the end of some other string. + * + * @param str The string to be modified + * @param suffix The suffix string to be appended onto `*str` + * @return true If the operation was successful + * @return false Otherwise + * + * If case of failure, `*str` is not modified. + */ +static inline bool +mstr_append(mstr *str, mstr_view suffix) +{ + return mstr_splice(str, str->len, 0, suffix); +} + +#define mstr_append(Into, Suffix) mstr_append((Into), mstr_view_from((Suffix))) + +/** + * @brief Append a single character to the given string object + * + * @param str The string object to be updated + * @param c The single character that will be inserted at the end + * @return true If the operation succeeded + * @return false Otherwise + * + * In case of failure, the string is not modified. + */ +static inline bool +mstr_append_char(mstr *str, char c) +{ + mstr_view one = mstr_view_data(&c, 1); + return mstr_append(str, one); +} + +/** + * @brief Replace every occurrence of `needle` in `str` with `sub` + * + * @param str The string object to be updated + * @param needle The non-empty needle string to be searched for.s + * @param sub The string to be inserted in place of each `needle` + * @return true If the operation succeeds + * @return false Otherwise + * + * @note If the `needle` string is empty, then the substitution string will + * be inserted around and between every byte in the string: + * + * replace("foo", "", "|") -> "|f|o|o|" + * + * @note The operation is guaranteed to never fail if the `sub` string is not + * longer than the `needle` string AND the needle and sub strings do not overlap + * + * @note If the operation fails, the content of `str` is an unspecified but valid + * string. + */ +static inline bool +mstr_replace(mstr *str, mstr_view needle, mstr_view sub) +{ + bool okay = true; + // We may dup the needle/sub if they overlap the output string + mstr needle_dup = mstr_null(); + mstr sub_dup = mstr_null(); + // Check if the needle is a substring of the target: + if (_mstr_overlaps(str, needle)) { + // Copy the needle string + needle_dup = mstr_copy(needle); + // Detect allocation failure: + okay = !!needle_dup.data; + // Update the needle to point to the duplicate: + needle = mstr_view_from(needle_dup); + } + // Do the same with the sub string: + if (okay && _mstr_overlaps(str, sub)) { + sub_dup = mstr_copy(sub); + okay = !!needle_dup.data; + sub = mstr_view_from(sub_dup); + } + // Scan forward, starting from the first position: + size_t off = 0; + while (okay && off <= str->len) { + // Find the next occurrence, starting from the scan offset + off = mstr_find(*str, needle, off); + if (off == SIZE_MAX) { + // No more occurrences. + break; + } + // Replace the needle string with the new value + if (!mstr_splice(str, off, needle.len, sub)) { + okay = false; + } + // Advance over the length of the replacement string, so we don't try to + // infinitely replace content if the replacement itself contains the needle + // string + if (mlib_unlikely(mlib_add(&off, sub.len))) { + // Integer overflow while advancing the offset. No good. + okay = false; + } + // Note: To support empty needles, advance one more space to avoid infinite + // repititions in-place. + // TODO: To do this "properly", this should instead advance over a full UTF-8-encoded + // codepoint. For now, just do a single byte. + if (!needle.len && mlib_unlikely(mlib_add(&off, 1))) { + // Advancing the extra distance failed + okay = false; + } + } + // Destroy the needle/sub strings, which we may have duplicated if they overlapped + // the target. If not, then these are a no-op. + mstr_destroy(&needle_dup); + mstr_destroy(&sub_dup); + return okay; +} + +/** + * @brief Like `mstr_sprintf`, but accepts a `va_list` directly. + */ +mlib_printf_attribute(1, 0) static inline mstr mstr_vsprintf(const char *format, va_list args) +{ + size_t format_strlen = strlen(format); + size_t sz = format_strlen; + if (mlib_unlikely(mlib_mul(&sz, 2))) { + // Overflow on multiply. Oof + sz = format_strlen; + } + + mstr ret = mstr_null(); + while (1) { + // Resize to make room for the formatted text + if (!mstr_resize(&ret, sz)) { + // Allocation failure + break; + } + + // Calc the size with the null terminator + size_t len_with_null = ret.len; + if (mlib_unlikely(mlib_add(&len_with_null, 1))) { + // Unlikely: Overflow + break; + } + + // Do the formatting + va_list dup_args; + va_copy(dup_args, args); + + // clang complains that the format string is not a string literal; + // this is fine since we're writing a vsnprintf wrapper + // the format string will be checked at call sites to mstr_vsprintf() + // because we mark it using mlib_printf_attribute +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-nonliteral" +#endif + int n_chars = vsnprintf(ret.data, len_with_null, format, dup_args); +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic pop +#endif + va_end(dup_args); + + // On error, returns a negative value + if (n_chars < 0) { + break; + } + + if ((size_t)n_chars <= ret.len) { + // Success. Truncate to the number of chars actually written: + mstr_resize(&ret, (size_t)n_chars); + // Return the successfully formatted string: + return ret; + } + + // Need more room. Resize and try again: + sz = (size_t)n_chars; + continue; + } + + // Only reached if the operation failed + mstr_destroy(&ret); + return ret; +} + +/** + * @brief Format a string according to `printf` rules + * + * @param f The format string to be used. + * @param ... The formatting arguments to interpolate into the string + * @return mstr A new mstr upon success, or a null mstr upon failure. + */ +mlib_printf_attribute(1, 2) static inline mstr mstr_sprintf(const char *f, ...) +{ + va_list args; + va_start(args, f); + mstr ret = mstr_vsprintf(f, args); + va_end(args); + return ret; +} + +/** + * @brief Like `mstr_sprintf_append`, but accepts the va_list directly. + */ +mlib_printf_attribute(2, 0) static inline bool mstr_vsprintf_append(mstr *string, const char *format, va_list args) +{ + mlib_check(string != NULL, because, "Output string parameter is required"); + mstr suffix = mstr_vsprintf(format, args); + bool ok = mstr_append(string, suffix); + mstr_destroy(&suffix); + return ok; +} + +/** + * @brief Append content to a string using `printf()` style formatting. + * + * @param string Pointer to a valid or null string object which will be modified + * @param format A printf-style format string to append onto `string` + * @param ... The interpolation arguments for `format` + * + * @retval true If-and-only-if the string is successfully modified + * @retval false If there was an error during formatting. The content of `string` + * is unspecified. + * + * This function maintains the existing content of `string` and only inserts + * additional characters at the end of the string. + */ +mlib_printf_attribute(2, 3) static inline bool mstr_sprintf_append(mstr *string, const char *format, ...) +{ + va_list args; + va_start(args, format); + const bool okay = mstr_vsprintf_append(string, format, args); + va_end(args); + return okay; +} + + +#endif // MLIB_STR_H_INCLUDED diff --git a/bsonjs/common/mlib/str_vec.h b/bsonjs/common/mlib/str_vec.h new file mode 100644 index 0000000..8a69e63 --- /dev/null +++ b/bsonjs/common/mlib/str_vec.h @@ -0,0 +1,31 @@ +/** + * @file str_vec.h + * @brief This file defines mstr_vec, a common "array of strings" type + * @date 2025-09-30 + * + * @copyright Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MLIB_STR_VEC_H_INCLUDED +#define MLIB_STR_VEC_H_INCLUDED + +#include +#include + +#define T mstr +#define VecDestroyElement(Ptr) (mstr_destroy(Ptr)) +#define VecCopyElement(Dst, Src) (*Dst = mstr_copy(*Src), Dst->data != NULL) +#include + +#endif // MLIB_STR_VEC_H_INCLUDED diff --git a/bsonjs/common/mlib/test.h b/bsonjs/common/mlib/test.h new file mode 100644 index 0000000..c0f937f --- /dev/null +++ b/bsonjs/common/mlib/test.h @@ -0,0 +1,334 @@ +/** + * @file mlib/test.h + * @brief Testing utilities + * @date 2025-01-30 + * + * @copyright Copyright (c) 2025 + * + * 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. + */ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +/** + * @brief Place this macro at the head of a (compound) statement to assert that + * executing that statement aborts the program with SIGABRT. + * + * Internally, this will fork the calling process and wait for the child process + * to terminate. It asserts that the child exits abnormally with SIGABRT. This + * test assertion is a no-op on Win32, since it does not have a suitable `fork` + * API. + * + * Beware that the child process runs in a forked environment, so it is not + * safe to use any non-fork-safe functionality, and any modifications to program + * state will not be visible in the parent. Behavior of attempting to escape the + * statement (goto/return) is undefined. + * + * If the child process does not abort, it will call `_Exit(71)` to indicate + * to the parent that it did not terminate (the number 71 is chosen arbitrarily) + * + * If the token `debug` is passed as a macro argument, then the forking behavior + * is suppressed, allowing for easier debugging of the statement. + */ +#define mlib_assert_aborts(...) MLIB_PASTE_3(_mlibAssertAbortsStmt, _, __VA_ARGS__)() + +#ifndef _WIN32 +#include +#include +#define _mlibAssertAbortsStmt_() \ + for (int once = 1, other_pid = fork(); once; once = 0) \ + for (; once; once = 0) \ + if (other_pid != 0) { \ + /* We are the parent */ \ + int wstatus; \ + waitpid(other_pid, &wstatus, 0); \ + if (WIFEXITED(wstatus)) { \ + /* Normal exit! */ \ + _mlib_stmt_did_not_abort(__FILE__, MLIB_FUNC, __LINE__, WEXITSTATUS(wstatus)); \ + } else if (WIFSIGNALED(wstatus)) { \ + /* Signalled */ \ + if (WTERMSIG(wstatus) != SIGABRT) { \ + fprintf(stderr, \ + "%s:%d: [%s]: Child process did not exit with SIGABRT! (Exited %d)\n", \ + __FILE__, \ + __LINE__, \ + MLIB_FUNC, \ + WTERMSIG(wstatus)); \ + fflush(stderr); \ + abort(); \ + } \ + } \ + } else /* We are the child */ \ + if ((fclose(stderr), 1)) \ + for (;; _Exit(71)) \ + for (;; _Exit(71)) /* Double loop to prevent the block from `break`ing out */ + +#else +#define _mlibAssertAbortsStmt_() \ + if (1) { \ + } else +#endif + +// Called when an assert-aborts statement does not terminate +static inline void +_mlib_stmt_did_not_abort(const char *file, const char *func, int line, int rc) +{ + /* Normal exit! */ + if (rc == 71) { + fprintf(stderr, "%s:%d: [%s]: Test case did not abort. The statement completed normally.\n", file, line, func); + } else { + fprintf(stderr, "%s:%d: [%s]: Test case did not abort (Exited %d)\n", file, line, func, rc); + } + fflush(stderr); + abort(); +} + +#define _mlibAssertAbortsStmt_debug() \ + for (;; _mlib_stmt_did_not_abort(__FILE__, MLIB_FUNC, __LINE__, -1)) \ + for (;; _mlib_stmt_did_not_abort(__FILE__, MLIB_FUNC, __LINE__, -1)) + +/** + * @brief Aggregate type that holds information about a source location + */ +typedef struct mlib_source_location { + const char *file; + int lineno; + const char *func; +} mlib_source_location; + +/** + * @brief Expands to an `mlib_source_location` for the location in which the macro is expanded + */ +#define mlib_this_source_location() (mlib_init(mlib_source_location){(__FILE__), (__LINE__), (MLIB_FUNC)}) +// ↑ The paren wrapping is required on VS2017 to prevent it from deleting the preceding comma (?!) + +/** + * @brief Evaluate a check, aborting with a diagnostic if that check fails + * + * Can be called with one argument to test a single boolean condition, or three + * arguments for more useful diagnostics with an infix operator. + */ +#define mlib_check(...) MLIB_ARGC_PICK(_mlib_check, #__VA_ARGS__, __VA_ARGS__) +// One arg: +#define _mlib_check_argc_2(ArgString, Condition) \ + _mlibCheckConditionSimple(Condition, ArgString, NULL, mlib_this_source_location()) +// Three args: +#define _mlib_check_argc_4(ArgString, A, Operator, B) \ + MLIB_NOTHING(#A, #B) MLIB_PASTE(_mlibCheckCondition_, Operator)(A, B, NULL) +// Five args: +#define _mlib_check_argc_6(ArgString, A, Operator, B, Infix, Reason) \ + MLIB_NOTHING(#A, #B) MLIB_PASTE(_mlib_check_with_suffix_, Infix)(A, Operator, B, Reason) +#define _mlib_check_with_suffix_because(A, Operator, B, Reason) \ + MLIB_NOTHING(#A, #B) MLIB_PASTE(_mlibCheckCondition_, Operator)(A, B, Reason) +// String-compare: +#define _mlibCheckCondition_str_eq(A, B, Reason) _mlibCheckStrEq(A, B, #A, #B, Reason, mlib_this_source_location()) +// Pointer-compare: +#define _mlibCheckCondition_ptr_eq(A, B, Reason) _mlibCheckPtrEq(A, B, #A, #B, Reason, mlib_this_source_location()) +// Integer-equal: +#define _mlibCheckCondition_eq(A, B, Reason) \ + _mlibCheckIntCmp(mlib_equal, \ + true, \ + "==", \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + #A, \ + #B, \ + Reason, \ + mlib_this_source_location()) +// Integer not-equal: +#define _mlibCheckCondition_neq(A, B, Reason) \ + _mlibCheckIntCmp(mlib_equal, \ + false, \ + "!=", \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + #A, \ + #B, \ + Reason, \ + mlib_this_source_location()) +// Integer comparisons: +#define _mlibCheckCondition_lt(A, B, Reason) \ + _mlibCheckIntCmp(mlib_less, \ + true, \ + "<", \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + #A, \ + #B, \ + Reason, \ + mlib_this_source_location()) +#define _mlibCheckCondition_lte(A, B, Reason) \ + _mlibCheckIntCmp(mlib_greater, \ + false, \ + "≤", \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + #A, \ + #B, \ + Reason, \ + mlib_this_source_location()) +#define _mlibCheckCondition_gt(A, B, Reason) \ + _mlibCheckIntCmp(mlib_greater, \ + true, \ + ">", \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + #A, \ + #B, \ + Reason, \ + mlib_this_source_location()) +#define _mlibCheckCondition_gte(A, B, Reason) \ + _mlibCheckIntCmp(mlib_less, \ + false, \ + "≥", \ + mlib_upsize_integer(A), \ + mlib_upsize_integer(B), \ + #A, \ + #B, \ + Reason, \ + mlib_this_source_location()) + + +// Simple assertion with an explanatory string +#define _mlibCheckCondition_because(Cond, Reason, _null) \ + _mlibCheckConditionSimple(Cond, #Cond, Reason, mlib_this_source_location()) + +/// Check evaluator when given a single boolean +static inline void +_mlibCheckConditionSimple(bool c, const char *expr, const char *reason, struct mlib_source_location here) +{ + if (!c) { + fprintf(stderr, "%s:%d: in [%s]: Check condition ⟨%s⟩ failed", here.file, here.lineno, here.func, expr); + if (reason) { + fprintf(stderr, " (%s)", reason); + } + fprintf(stderr, "\n"); + fflush(stderr); + abort(); + } +} + +// Implement integer comparison checks +static inline void +_mlibCheckIntCmp(enum mlib_cmp_result cres, // The cmp result to check + bool cond, // Whether we expect the cmp result to match `cres` + const char *operator_str, + struct mlib_upsized_integer left, + struct mlib_upsized_integer right, + const char *left_expr, + const char *right_expr, + const char *reason, + struct mlib_source_location here) +{ + if (((mlib_cmp)(left, right, 0) == cres) != cond) { + fprintf(stderr, + "%s:%d: in [%s]: Check [⟨%s⟩ %s ⟨%s⟩] failed:\n", + here.file, + here.lineno, + here.func, + left_expr, + operator_str, + right_expr); + fprintf(stderr, " "); + if (left.is_signed) { + fprintf(stderr, "%lld", (long long)left.bits.as_signed); + } else { + fprintf(stderr, "%llu", (unsigned long long)left.bits.as_unsigned); + } + fprintf(stderr, " ⟨%s⟩\n", left_expr); + fprintf(stderr, " "); + if (right.is_signed) { + fprintf(stderr, "%lld", (long long)right.bits.as_signed); + } else { + fprintf(stderr, "%llu", (unsigned long long)right.bits.as_unsigned); + } + fprintf(stderr, " ⟨%s⟩\n", right_expr); + if (reason) { + fprintf(stderr, "Because: %s\n", reason); + } + fflush(stderr); + abort(); + } +} + +// Pointer-comparison +static inline void +_mlibCheckPtrEq(const void *left, + const void *right, + const char *left_expr, + const char *right_expr, + const char *reason, + struct mlib_source_location here) +{ + if (left != right) { + fprintf(stderr, + "%s:%d: in [%s]: Check [⟨%s⟩ pointer-equal ⟨%s⟩] failed:\n", + here.file, + here.lineno, + here.func, + left_expr, + right_expr); + fprintf(stderr, + " %p ⟨%s⟩\n" + " ≠ %p ⟨%s⟩\n", + left, + left_expr, + right, + right_expr); + if (reason) { + fprintf(stderr, "Because: %s\n", reason); + } + fflush(stderr); + abort(); + } +} + +// String-comparison +static inline void +_mlibCheckStrEq(const char *left, + const char *right, + const char *left_expr, + const char *right_expr, + const char *reason, + struct mlib_source_location here) +{ + if (strcmp(left, right)) { + fprintf(stderr, + "%s:%d: in [%s]: Check [⟨%s⟩ str-equal ⟨%s⟩] failed:\n", + here.file, + here.lineno, + here.func, + left_expr, + right_expr); + fprintf(stderr, + " “%s” ⟨%s⟩\n" + " ≠ “%s” ⟨%s⟩\n", + left, + left_expr, + right, + right_expr); + if (reason) { + fprintf(stderr, "Because: %s\n", reason); + } + fflush(stderr); + abort(); + } +} diff --git a/bsonjs/common/mlib/time_point.h b/bsonjs/common/mlib/time_point.h new file mode 100644 index 0000000..7c8a5ac --- /dev/null +++ b/bsonjs/common/mlib/time_point.h @@ -0,0 +1,346 @@ +/** + * @file mlib/time_point.h + * @brief A point-in-time type + * @date 2025-04-17 + * + * The `mlib_time_point` type represents a stable point-in-time. The time point + * itself is relative to a monotonic clock for the program, so it should not be + * transmitted or persisted outside of the execution of a program that uses it. + * + * @copyright Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MLIB_TIME_POINT_H_INCLUDED +#define MLIB_TIME_POINT_H_INCLUDED + +#include +#include +#include +#include + +// Check for POSIX clock functions functions +#undef mlib_have_posix_clocks +#define mlib_have_posix_clocks() 0 +#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_DEFAULT_SOURCE) && !defined(_WIN32)) +#include +#undef mlib_have_posix_clocks +#define mlib_have_posix_clocks() 1 +#endif + +#include +#include +#include + +mlib_extern_c_begin(); + +/** + * @brief An abstract point-in-time type + * + * The time point is encoded as a duration relative to some stable reference point + * provided by the system. See the docs for the `time_since_monotonic_start` + * member for more details. + * + * At time of writing, there is no easy way to convert this monotonic time point + * into a human-readable wall time. Thus, the time-point itself is abstract. + */ +typedef struct mlib_time_point { + /** + * @brief The encoding of the time point as a duration relative to some + * unspecified stable real point in time. + * + * It is important to understand the nature of the reference point: `mlib_now()` + * uses the system's monotonic high-resolution clock, which has an unspecified + * reference point in the past. That stable reference point may change between + * program executions, so it is not safe to store/transmit this value outside + * of the current program execution. + * + * If you attempt to store a duration in this member that is with respect to + * some other clock, then the resulting time point object will have an unspecified + * relationship to other time points created with different clocks. For this reason, + * this member should not be set to any absolute values, and should only be adjusted + * relative to its current value. + */ + mlib_duration time_since_monotonic_start; +} mlib_time_point; + +/** + * @brief Given two time points, selects the time point that occurs earliest + */ +static inline mlib_time_point +mlib_earliest(mlib_time_point l, mlib_time_point r) +{ + l.time_since_monotonic_start = mlib_duration(l.time_since_monotonic_start, min, r.time_since_monotonic_start); + return l; +} + +/** + * @brief Given two time points, selects the time point that occurs later + */ +static inline mlib_time_point +mlib_latest(mlib_time_point l, mlib_time_point r) +{ + l.time_since_monotonic_start = mlib_duration(l.time_since_monotonic_start, max, r.time_since_monotonic_start); + return l; +} + +/** + * @brief Obtain the integer clock ID that is used by `mlib_now()` to obtain + * the time. This value only has meaning on POSIX systems. On Win32, returns + * INT_MIN. + * + * @return int The integer clock ID, corresponding to the value of a `CLOCK_...` + * object macro. + */ +static inline int +mlib_now_clockid(void) mlib_noexcept +{ +#ifdef CLOCK_MONOTONIC_RAW + // Linux had a bad definition of CLOCK_MONOTONIC, which would jump based on NTP adjustments. + // They replaced it with CLOCK_MONOTONIC_RAW, which is stable and cannot be adjusted. + return CLOCK_MONOTONIC_RAW; +#elif defined(CLOCK_MONOTONIC) + return CLOCK_MONOTONIC; +#else + return INT_MIN; +#endif +} + +/** + * @brief Obtain a point-in-time corresponding to the current time + */ +static inline mlib_time_point +mlib_now(void) mlib_noexcept +{ +#if mlib_have_posix_clocks() + // Use POSIX clock_gettime + struct timespec ts; + int rc = clock_gettime(mlib_now_clockid(), &ts); + // The above call must never fail: + mlib_check(rc, eq, 0); + // Encode the time point: + mlib_time_point ret; + ret.time_since_monotonic_start = mlib_duration_from_timespec(ts); + return ret; +#elif mlib_is_win32() + // Win32 APIs for the high-performance monotonic counter. These APIs never fail after Windows XP + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + LARGE_INTEGER lits; + QueryPerformanceCounter(&lits); + // Number of ticks of the perf counter + const int64_t ticks = lits.QuadPart; + // Number of ticks that the counter emits in one second + const int64_t ticks_per_second = freq.QuadPart; + // Do some math that avoids an integer overflow when converting to microseconds. + // Just one million, used to convert time units to microseconds. + const int64_t one_million = 1000000; + // Number of whole seconds that have elapsed: + const int64_t whole_seconds = ticks / ticks_per_second; + // Number of microseconds beyond the last whole second: + const int64_t subsecond_us = ((ticks % ticks_per_second) * one_million) / ticks_per_second; + mlib_time_point ret; + ret.time_since_monotonic_start = mlib_duration((whole_seconds, s), plus, (subsecond_us, us)); + return ret; +#else +#error We do not know how to get the current time on this platform +#endif +} + +/** + * @brief Obtain a point-in-time relative to a base time offset by the given + * duration (which may be negative). + * + * @param from The basis of the time offset + * @param delta The amount of time to shift the resulting time point + * @return mlib_time_point If 'delta' is a positive duration, the result is a + * point-in-time *after* 'from'. If 'delta' is a negative duration, the result + * is a point-in-time *before* 'from'. + */ +static inline mlib_time_point +mlib_time_add(mlib_time_point from, mlib_duration delta) mlib_noexcept +{ + mlib_time_point ret; + ret.time_since_monotonic_start = mlib_duration(from.time_since_monotonic_start, plus, delta); + return ret; +} + +#define mlib_time_add(From, Delta) mlib_time_add(From, mlib_duration_arg(Delta)) + +/** + * @brief Obtain the duration between two points in time. + * + * @param stop The target time + * @param start The base time + * @return mlib_duration The amount of time you would need to wait starting + * at 'start' for the time to become 'stop' (the result may be a negative + * duration). + * + * Intuition: If "stop" is "in the future" relative to "start", you will + * receive a positive duration, indicating an amount of time to wait + * beginning at 'start' to reach 'stop'. If "stop" is actually *before* + * "start", you will receive a paradoxical *negative* duration, indicating + * the amount of time needed to time-travel backwards to reach "stop." + */ +static inline mlib_duration +mlib_time_difference(mlib_time_point stop, mlib_time_point start) +{ + return mlib_duration(stop.time_since_monotonic_start, minus, start.time_since_monotonic_start); +} + +/** + * @brief Obtain the amount of time that has elapsed since the time point `t`, + * or a negative duration if the time is in the future. + * + * @param t The time point to be inspected + * @return mlib_duration If `t` is in the past, returns the duration of time + * that has elapsed since that point-in-time. If `t` is in the future, returns + * a negative time representing the amount of time that be waited until we + * reach `t`. + */ +static inline mlib_duration +mlib_elapsed_since(mlib_time_point t) +{ + return mlib_time_difference(mlib_now(), t); +} + +/** + * @brief Compare two time points to create an ordering. + * + * A time point "in the past" is "less than" a time point "in the future". + * + * @retval <0 If 'a' is before 'b' + * @retval >0 If 'b' is before 'a' + * @retval 0 If 'a' and 'b' are equivalent + * + * @note This is a function-like macro that can be called with an infix operator + * as the second argument to do natural time-point comparisons: + * + * ``` + * mlib_time_cmp(a, <=, b) + * ``` + */ +static inline enum mlib_cmp_result +mlib_time_cmp(mlib_time_point a, mlib_time_point b) mlib_noexcept +{ + return mlib_duration_cmp(a.time_since_monotonic_start, b.time_since_monotonic_start); +} + +#define mlib_time_cmp(...) MLIB_ARGC_PICK(_mlib_time_cmp, __VA_ARGS__) +#define _mlib_time_cmp_argc_2 mlib_time_cmp +#define _mlib_time_cmp_argc_3(L, Op, R) (mlib_time_cmp((L), (R)) Op 0) + +/** + * @brief Pause the calling thread until at least the specified duration has elapsed. + * + * @param d The duration of time to pause the thread. If this duration is zero + * or negative, then this function returns immediately. + * @return int An error code, if any occurred. Returns zero upon success, or + * the system's error number value (`errno` on POSIX, `GetLastError()` on + * Windows) + */ +static inline int +mlib_sleep_for(const mlib_duration d) mlib_noexcept +{ + mlib_duration_rep_t duration_usec = mlib_microseconds_count(d); + if (duration_usec <= 0) { + // Don't sleep any time + return 0; + } +#if mlib_have_posix_clocks() + // Convert the microseconds count to the value for the usleep function. We don't + // know the precise integer type that `usleep` expects, so do a checked-narrow + // to handle too-large values. + useconds_t i = 0; + if (mlib_narrow(&i, duration_usec)) { + // Too many microseconds. Sleep for the max. This will only be reached + // for positive durations because of the above check against `<= 0` + i = mlib_maxof(useconds_t); + } + int rc = usleep(i); + if (rc != 0) { + return errno; + } + return 0; +#elif defined(_WIN32) + DWORD retc = 0; + // Use WaitableTimer + const HANDLE timer = CreateWaitableTimerW(/* no attributes */ NULL, + /* Manual reset */ true, + /* Unnamed */ NULL); + // Check that we actually succeeded in creating a timer. + if (!timer) { + retc = GetLastError(); + goto done; + } + // Convert the number of microseconds into a count of 100ns intervals. Use + // a negative value to request a relative sleep time. + LONGLONG negative_n_100ns_units = 0; + if (mlib_mul(&negative_n_100ns_units, duration_usec, -10)) { + // Too many units. Clamp to the max duration (negative for a relative + // sleep): + negative_n_100ns_units = mlib_minof(LONGLONG); + } + LARGE_INTEGER due_time; + due_time.QuadPart = negative_n_100ns_units; + BOOL okay = SetWaitableTimer(/* The timer to modify */ timer, + /* The time after which it will fire */ &due_time, + /* Interval period 0 = only fire once */ 0, + /* No completion routine */ NULL, + /* No arg for no completion routine */ NULL, + /* Wake up the system if it goes to sleep */ true); + if (!okay) { + // Failed to set the timer. Hmm? + retc = GetLastError(); + goto done; + } + // Do the actual wait + DWORD rc = WaitForSingleObject(timer, INFINITE); + if (rc == WAIT_FAILED) { + // Executing the wait operation failed. + retc = GetLastError(); + goto done; + } + // Check for success: + mlib_check(rc, eq, WAIT_OBJECT_0); +done: + // Done with the timer. + if (timer) { + CloseHandle(timer); + } + return retc; +#else +#error "mlib_sleep_for" is not implemented on this platform. +#endif +} + +#define mlib_sleep_for(...) mlib_sleep_for(mlib_duration(__VA_ARGS__)) + +/** + * @brief Pause the calling thread until the given time point has been reached + * + * @param when The time point at which to resume, at soonest + * @return int A possible error code for the operation. Returns zero upon success. + * + * The `when` is the *soonest* successful wake time. The thread may wake at a later time. + */ +static inline int +mlib_sleep_until(const mlib_time_point when) mlib_noexcept +{ + const mlib_duration time_until = mlib_time_difference(when, mlib_now()); + return mlib_sleep_for(time_until); +} + +mlib_extern_c_end(); + +#endif // MLIB_TIME_POINT_H_INCLUDED diff --git a/bsonjs/common/mlib/timer.h b/bsonjs/common/mlib/timer.h new file mode 100644 index 0000000..41edd2d --- /dev/null +++ b/bsonjs/common/mlib/timer.h @@ -0,0 +1,173 @@ +/** + * @file mlib/timer.h + * @brief Timer types and functions + * @date 2025-04-18 + * + * This file contains APIs for creating fixed-deadline timer objects that represent + * stable expiration points. + * + * @copyright Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MLIB_TIMER_H_INCLUDED +#define MLIB_TIMER_H_INCLUDED + +#include +#include +#include + +mlib_extern_c_begin(); + +/** + * @brief Represents an expiry timer. The timer stores some point-in-time + * after which it is considered to have "expired." + */ +typedef struct mlib_timer { + /** + * @brief The point-in-time at which the timer will be considered expired. + * + * This field can be updated or modified to change the expiration time of + * the timer. + */ + mlib_time_point expires_at; +} mlib_timer; + +/** + * @brief Create a deadline timer that expires at the given point-in-time + * + * @param t The point-in-time at which the returned timer should be expired + * @return mlib_timer + */ +static inline mlib_timer +mlib_expires_at(const mlib_time_point t) mlib_noexcept +{ + mlib_timer ret; + ret.expires_at = t; + return ret; +} + +/** + * @brief Create a deadline timer that expires after the given duration has + * elapsed from the point-in-time at which this function is called + */ +static inline mlib_timer +mlib_expires_after(const mlib_duration dur) mlib_noexcept +{ + const mlib_time_point later = mlib_time_add(mlib_now(), dur); + return mlib_expires_at(later); +} + +#define mlib_expires_after(...) mlib_expires_after(mlib_duration(__VA_ARGS__)) + +/** + * @brief Obtain a timer that will "never" expire + * + * In actuality, the timer expires at a time so far in the future that no computer + * program could ever hope to continue running to that point, and by the time + * that point is reached it will be some other civilization's problem. + */ +static inline mlib_timer +mlib_expires_never(void) mlib_noexcept +{ + mlib_timer t; + t.expires_at.time_since_monotonic_start = mlib_duration_max(); + return t; +} + +/** + * @brief Between two timers, return the timer that will expire the soonest + */ +static inline mlib_timer +mlib_soonest_timer(mlib_timer l, mlib_timer r) mlib_noexcept +{ + l.expires_at = mlib_earliest(l.expires_at, r.expires_at); + return l; +} + +/** + * @brief Obtain the duration of time that is remaining until the given timer + * expires. If the timer has expired, the returned duration will be zero (never + * negative) + */ +static inline mlib_duration +mlib_timer_remaining(const mlib_timer timer) mlib_noexcept +{ + // The duration until the expiry time of the timer + const mlib_duration remain = mlib_time_difference(timer.expires_at, mlib_now()); + if (mlib_duration_cmp(remain, <, mlib_duration())) { + // No time remaining. Return a zero duration (not a negative duration) + return mlib_duration(); + } + return remain; +} + +/** + * @brief Test for timer expiration + * + * @param timer The timer to be tested + * @param once (Optional) A pointer to an optional once-flag that will be set + * to `true` (see below) + * + * The function behaves as follows: + * + * - If `once` is a null pointer, then returns a boolean indicating whether the + * timer has expired. + * - Otherwise, if `*once` is `true`: + * - If `timer` has expired, returns `true` + * - Otherwise, `*once` is set to `true` and returns `false` + * + * The intent of the `once` flag is to support loops that check for expiry, + * where at least one iteration of the loop *must* be attempted, even if the + * timer has expired. For example: + * + * ``` + * void do_thing() { + * bool once = false; + * while (!mlib_timer_is_expired(timer, &once)) { + * try_thing(timer); + * } + * } + * ``` + * + * In the above, `try_thing` will be called *at least once*, even if the timer + * is already expired. + */ +static inline bool +mlib_timer_is_expired(const mlib_timer timer, bool *once) mlib_noexcept +{ + // Is the timer already expired? + const bool no_time_remaining = mlib_time_cmp(timer.expires_at, <=, mlib_now()); + if (!once) { + // Just return `true` if there is zero time remaining + return no_time_remaining; + } else { + // Tweak behavior based on the `*once` value + if (!*once) { + // This is the first time we have been called with the given once-flag + *once = true; + // Don't count an expiration, even if we have zero time left, because + // the caller wants to try some operation at least once + return false; + } + return no_time_remaining; + } +} + +mlib_extern_c_end(); + +#define mlib_timer_is_expired(...) MLIB_ARGC_PICK(_mlibTimerIsExpired, __VA_ARGS__) +#define _mlibTimerIsExpired_argc_1(Timer) mlib_timer_is_expired((Timer), NULL) +#define _mlibTimerIsExpired_argc_2(Timer, OncePtr) mlib_timer_is_expired((Timer), (OncePtr)) + +#endif // MLIB_TIMER_H_INCLUDED diff --git a/bsonjs/common/mlib/vec.th b/bsonjs/common/mlib/vec.th new file mode 100644 index 0000000..65f243a --- /dev/null +++ b/bsonjs/common/mlib/vec.th @@ -0,0 +1,470 @@ +/** + * @file vec.th + * @brief Declare a new vector container data type + * @date 2024-10-02 + * + * To use this file: + * + * - #define a type `T` immediately before including this file. + * - Optional: Define an identifier `VecName` to the name of the vector. If unset, declares `_vec` + * - Optional: Define a `VecDestroyElement(Ptr)` macro to specify how the vector + * should destroy the element at `*Ptr`. If unset, destroying is a no-op. + * - Optional: Define `VecInitElement(Ptr, ...)` which initializes a new element. + * The first macro argument is a pointer to the element and subsequent arguments + * are unspecified and reserved for future use. Elements are zero-initialized + * before being passed to this macro. + * - Optional: Define `VecCopyElement(DstPtr, SrcPtr)` to copy data from `*SrcPtr` + * to `*DstPtr`. The vector's copying function is only defined if this macro + * is defined. This macro MUST evaluate to a boolean to indicate if the copy + * operation succeeded. If a copy fails, then the partially copied elements + * will be destroyed and the overall copy will fail. + * + * To add a trival copying function, define `VecCopyElement` to + * `VecTrivialCopyElement`. + * + * - NOTE: All of the above macros will be automatically undef'd after this file + * is included. + * + * Types stored in the vector must be trivially relocatable. + * + * @copyright Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +#include // assert +#include // bool +#include // size_t +#include // calloc, realloc, free +#include // memcpy, memset + +// Check that the caller provided a `T` macro to be the element type +#ifndef T +#if defined(__clangd__) || defined(__INTELLISENSE__) +#define T int // Define a type for IDE diagnostics +#define VecCopyElement VecTrivialCopyElement // For IDE highlighting +#else +#error A type `T` should be defined before including this file +#endif +#endif + +#ifndef VecName +#define VecName MLIB_PASTE(T, _vec) +#endif + +#ifndef VecDestroyElement +#define VecDestroyElement(Ptr) ((void)(Ptr)) +#endif + +#ifndef VecInitElement +#define VecInitElement(Ptr) ((void)(Ptr)) +#endif + +#ifndef VecTrivialCopyElement +#define VecTrivialCopyElement(DstPtr, SrcPtr) ((*(DstPtr) = *(SrcPtr)), true) +#endif + +#pragma push_macro("vec_inline_spec") +#if !defined(vec_inline_spec) +#define vec_inline_spec static inline +#endif + +// The "fn" macro just adds a qualified name to the front of a function identifier +#pragma push_macro("fn") +#undef fn +#define fn(M) MLIB_PASTE_3(VecName, _, M) + +typedef struct VecName { + /** + * @private + * @brief Pointer to the first vector element, or NULL if the vector is + * empty. + * + * @note DO NOT DIRECTLY MODIFY THIS VALUE + */ + T *data; + /** + * @brief The number of elements in the vector. + * + * @note DO NOT DIRECTLY MODIFY THIS VALUE + */ + size_t size; + /** + * @brief The number of allocated storage elements. + * + * @note DO NOT DIRECTLY MODIFY THIS VALUE + */ + size_t capacity; + +#if mlib_is_cxx() + T * + begin() noexcept + { + return data; + } + T * + end() noexcept + { + return data ? data + size : data; + } +#endif +} VecName; + +mlib_extern_c_begin(); + +/** + * @brief Obtain a pointer-to-mutable to the first element in the given vector + */ +vec_inline_spec T * +fn(begin(VecName *v)) mlib_noexcept +{ + return v->data; +} + +/** + * @brief Obtain a pointer-to-mutable past the last element in the given vector + */ +vec_inline_spec T * +fn(end(VecName *v)) mlib_noexcept +{ + return v->data ? v->data + v->size : v->data; +} + +/** + * @brief Obtain a pointer-to-const to the first element in the given vector + */ +vec_inline_spec T const * +fn(cbegin(VecName const *v)) mlib_noexcept +{ + return v->data; +} + +/** + * @brief Obtain a pointer-to-const past the last element in the given vector + */ +vec_inline_spec T const * +fn(cend(VecName const *v)) mlib_noexcept +{ + return v->data ? v->data + v->size : v->data; +} + +/** + * @brief Get the maximum number of elements that can be held in the vector of + * a certain type. + */ +vec_inline_spec size_t +fn(max_size(void)) mlib_noexcept +{ + // We compare against (signed) PTRDIFF_MAX because want to support the difference + // between two pointers. If we use the unsigned size, then we could have vectors + // with size that is too large to represent the difference between two sizes. + return PTRDIFF_MAX / sizeof(T); +} + +/** + * @brief Set the capacity of the given vector. + * + * @param self The vector object to be modified + * @param count The new capacity. If this is less than the current size, then + * the capacity will be capped at the size instead + * + * @retval true If-and-only-if the reallocation was successful + * @retval false If there was an error in allocating the buffer + */ +vec_inline_spec bool +fn(reserve(VecName *const self, size_t count)) mlib_noexcept +{ + // Check if this value is beyond the possible capacity of the vector + if (count > fn(max_size())) { + // Too many elements. We cannot allocate a region this large. + return false; + } + // Check if we are already at the requested capacity. + if (count == self->capacity) { + // No reallocation needed. + return true; + } + // Check if the caller is requesting a lower capacity than our current size + if (count < self->size) { + // We cannot shrink the capacity below the current size, so just shrink-to-fit + count = self->size; + } + // Impossible: We will never shrink below `self.size`, and if + // `self.size == 0` and `count == 0`, then we early-return'd above. + assert(count != 0); + // The number of bytes we need to allocate. Note that this cannot overflow + // because we guard against it by checking against `max_size()` + const size_t new_buffer_size = count * sizeof(T); + // Attempt to reallocate the region + T *const new_buffer = (T *)realloc(self->data, new_buffer_size); + if (!new_buffer) { + // Failed to reallocate a new storage region + return false; + } + // Successfully reallocated the buffer. Update our storage pointer. + self->data = new_buffer; + // Note the new capacity. + self->capacity = count; + return true; +} + +/** + * @brief Destroy elements in the vector at the specified range positions + * + * @param self The vector to be updated + * @param first Pointer to the first element to be destroyed + * @param last Pointer to the first element to NOT be destroyed + * + * Elements are destroyed and removed starting at the end. If `first == last`, + * this is a no-op. The given pointers must refer to vector elements, and `last` + * must be reachable by advancing `first` zero or more times. + */ +vec_inline_spec void +fn(erase(VecName *const self, T *const first, T *const last)) +{ + // Number of elements following the removed region + const size_t n_tail_elements = (size_t)(fn(end(self)) - last); + // Destroy elements in reverse order: + for (T *r_iter = last; r_iter != first; --r_iter) { + VecDestroyElement((r_iter - 1)); + --self->size; + } + // This mult cannot overflow because we can never contain enough elements to overflow PTRDIFF_MAX + const size_t n_bytes_to_shift = n_tail_elements * sizeof(T); + // If there are any elements to be shifted, shift them down over the removed region + if (n_bytes_to_shift) { + // Shift all tail elements down into their new position + memmove(first, last, n_bytes_to_shift); + } +} + +/** + * @brief Destroy a single element at the given zero-based index position + */ +vec_inline_spec void +fn(erase_at(VecName *const self, size_t pos)) +{ + fn(erase(self, fn(begin(self)) + pos, fn(end(self)))); +} + +/** + * @brief Resize the vector to hold the given number of elements + * + * Newly added elements are zero-initialized, or initailized using VecInitElement + * + * @retval true If-and-only-if the resize was successful + * @retval false If the function failed to allocate the new storage region + * + * @note Don't forget to check the return value for success! + */ +// mlib_nodiscard ("Check the returned bool to detect allocation failure") +vec_inline_spec bool +fn(resize(VecName *const self, size_t const count)) mlib_noexcept +{ + // Check if we aren't actually growing the vector. + if (count <= self->size) { + // We need to destroy elements at the tail. If `count == size`, this is a no-op. + if (self->data) { + fn(erase(self, fn(begin(self)) + count, fn(end(self)))); + } + return true; + } + + // We need to increase the capacity of the vector to hold the new elements + // Try to auto-grow capacity. Increase capacity by ×1.5 + const size_t half_current_capacity = self->capacity / 2; + size_t new_capacity = 0; + if (mlib_unlikely(mlib_add(&new_capacity, self->size, half_current_capacity))) { + // The auto growth amount would overflow, so just cap to the max size. + new_capacity = fn(max_size()); + } + // Check if our automatic growth is big enough to hold the requested number of elements + if (new_capacity < count) { + // The automatic growth factor is actually smaller than the number of new elements + // the caller wants, so we need to increase capacity to that level instead. + new_capacity = count; + } + // Try to reserve more storage + if (!fn(reserve(self, new_capacity))) { + // We failed to reserve the new storage region. The requested capacity may be too large, + // or we may have just run out of memory. + return false; + } + + // Pointer to where the new end will be + T *const new_end = fn(begin(self)) + count; + // Create a zero-initialized object to copy over the top of each new element. + T zero; + memset(&zero, 0, sizeof zero); + // Call init() on ever new element up until the new size + for (T *iter = fn(end(self)); iter != new_end; ++iter) { + *iter = zero; + (void)(VecInitElement((iter))); + } + + // Update the stored size + self->size = count; + return true; +} + +/** + * @brief Append another element, returning a pointer to that element. + * + * @return T* A pointer to the newly added element, or NULL in case of allocation failure. + */ +// mlib_nodiscard ("Check the returned pointer for failure") +vec_inline_spec T * +fn(push(VecName *self)) mlib_noexcept +{ + size_t count = self->size; + if (mlib_unlikely(mlib_add(&count, 1))) { + // Adding another element would overflow size_t. This is extremely unlikely, + // but precautionary. + return NULL; + } + if (!fn(resize(self, count))) { + // Failed to push another item + return NULL; + } + return fn(begin(self)) + count - 1; +} + +/** + * @brief Create a new empty vector + */ +vec_inline_spec VecName fn(new(void)) mlib_noexcept +{ + VecName ret = {NULL, 0, 0}; + return ret; +} + +/** + * @brief Destroy the pointed-to vector, freeing the associated data buffer. + * + * The pointed-to vector becomes valid storage for a new vector object. + */ +vec_inline_spec void +fn(destroy(VecName *self)) mlib_noexcept +{ + // Resizing to zero will destroy all elements + (void)fn(resize(self, 0)); + // Resizing won't necessarily free the data buffer. Do that now. + free(self->data); + self->capacity = 0; + self->data = NULL; +} + +/** + * @brief Create a new vector with `n` initialized elements + */ +vec_inline_spec VecName +fn(new_n(size_t n, bool *okay)) mlib_noexcept +{ + VecName ret = fn(new()); + *okay = fn(resize)(&ret, n); + return ret; +} + +#ifdef VecCopyElement +/** + * @brief Copy the data from the vector `src` into storage for a new vector `dst` + * + * @param dst_vec Pointer-to-storage for a new vector object to be initialized. + * @param src_vec Pointer to a vector whose elements will be copied into a new vector + * @retval true If-and-only-if the copy was successful. + * @retval false Otherwise + */ +vec_inline_spec bool +fn(init_copy(VecName *dst_vec, VecName const *src_vec)) mlib_noexcept +{ + VecName tmp = fn(new()); + // Try to reseve capacity for all new elements. Don't resize(), because we want + // uninitialized storage for the new data. + if (!fn(reserve(&tmp, src_vec->size))) { + // We failed to reserve capacity in the new vector + fn(destroy(&tmp)); + // Always leave `dst_vec` in an initialized state. + *dst_vec = (VecName){NULL, 0, 0}; + return false; + } + // Copy everything into the destination element-by-element + { + // Input iterator + T const *in_iter = fn(cbegin(src_vec)); + // Input stop position + T const *const in_stop = fn(cend(src_vec)); + // Output iterator + T *out_iter = tmp.data; + // Copy from the first to the last + for (; in_iter != in_stop; ++in_iter, ++out_iter) { + // Try to copy into the new element + if (!VecCopyElement((out_iter), (in_iter))) { + // Failed copying here. Undo everything by destroying the temporary + fn(destroy(&tmp)); + // Always leave `dst_vec` in an initialized state. + *dst_vec = (VecName){NULL, 0, 0}; + return false; + } + // Update the size of the temporary vec to record that it is holding the new + // element. This allows us to call `destroy()` to undo our work. + tmp.size++; + } + } + // Everything went okay. Give the temporary to the caller as the final result + *dst_vec = tmp; + return true; +} +#endif // VecCopyElement + +#ifndef mlib_vec_foreach +#define mlib_vec_foreach(Type, VarName, Vector) \ + for (Type *VarName = (Vector).data; VarName && (VarName != (Vector).data + (Vector).size); ++VarName) +#endif + +#ifndef mlib_vec_at +/** + * @brief Obtain a vector element at some zero-based index offset, with negative index + * wrapping (-1 refers to the last element in the vector) + * + * @note The `Vec` argument will be evaluated at least twice! + */ +#define mlib_vec_at(Vec, Pos) ((Vec).data[_mlib_vec_index_adjust((Vec).size, mlib_upsize_integer(Pos))]) +static inline size_t +_mlib_vec_index_adjust(size_t size, mlib_upsized_integer pos) +{ + if (pos.is_signed && pos.bits.as_signed < 0) { + return mlib_assert_add(size_t, size, pos.bits.as_signed); + } + mlib_check(pos.bits.as_unsigned, lte, size, because, "the vector index must be in-bounds for mlib_vec_at()"); + return pos.bits.as_unsigned; +} +#endif + +mlib_extern_c_end(); + +#undef T +#undef VecName +#undef VecDestroyElement +#undef VecInitElement +#undef VecTrivialCopyElement +#ifdef VecCopyElement +#undef VecCopyElement +#endif +// These ones we want to pop, not undefine: +#pragma pop_macro("fn") +#pragma pop_macro("vec_inline_spec") + +// vi: ft=c diff --git a/bsonjs/jsonsl/jsonsl.c b/bsonjs/jsonsl/jsonsl.c index a7bb8f4..92e6b73 100644 --- a/bsonjs/jsonsl/jsonsl.c +++ b/bsonjs/jsonsl/jsonsl.c @@ -3,83 +3,12 @@ * See included LICENSE file for license details. */ -#include "jsonsl.h" -#include "bson/bson-memory.h" +#include +#include #include #include -#ifdef JSONSL_USE_METRICS -#define XMETRICS \ - X(STRINGY_INSIGNIFICANT) \ - X(STRINGY_SLOWPATH) \ - X(ALLOWED_WHITESPACE) \ - X(QUOTE_FASTPATH) \ - X(SPECIAL_FASTPATH) \ - X(SPECIAL_WSPOP) \ - X(SPECIAL_SLOWPATH) \ - X(GENERIC) \ - X(STRUCTURAL_TOKEN) \ - X(SPECIAL_SWITCHFIRST) \ - X(STRINGY_CATCH) \ - X(NUMBER_FASTPATH) \ - X(ESCAPES) \ - X(TOTAL) \ - -struct jsonsl_metrics_st { -#define X(m) \ - unsigned long metric_##m; - XMETRICS -#undef X -}; - -static struct jsonsl_metrics_st GlobalMetrics = { 0 }; -static unsigned long GenericCounter[0x100] = { 0 }; -static unsigned long StringyCatchCounter[0x100] = { 0 }; - -#define INCR_METRIC(m) \ - GlobalMetrics.metric_##m++; - -#define INCR_GENERIC(c) \ - INCR_METRIC(GENERIC); \ - GenericCounter[c]++; \ - -#define INCR_STRINGY_CATCH(c) \ - INCR_METRIC(STRINGY_CATCH); \ - StringyCatchCounter[c]++; - -JSONSL_API -void jsonsl_dump_global_metrics(void) -{ - int ii; - printf("JSONSL Metrics:\n"); -#define X(m) \ - printf("\t%-30s %20lu (%0.2f%%)\n", #m, GlobalMetrics.metric_##m, \ - (float)((float)(GlobalMetrics.metric_##m/(float)GlobalMetrics.metric_TOTAL)) * 100); - XMETRICS -#undef X - printf("Generic Characters:\n"); - for (ii = 0; ii < 0xff; ii++) { - if (GenericCounter[ii]) { - printf("\t[ %c ] %lu\n", ii, GenericCounter[ii]); - } - } - printf("Weird string loop\n"); - for (ii = 0; ii < 0xff; ii++) { - if (StringyCatchCounter[ii]) { - printf("\t[ %c ] %lu\n", ii, StringyCatchCounter[ii]); - } - } -} - -#else -#define INCR_METRIC(m) -#define INCR_GENERIC(c) -#define INCR_STRINGY_CATCH(c) -JSONSL_API -void jsonsl_dump_global_metrics(void) { } -#endif /* JSONSL_USE_METRICS */ - #define CASE_DIGITS \ case '1': \ case '2': \ @@ -99,7 +28,6 @@ static int is_allowed_escape(unsigned); static int is_simple_char(unsigned); static char get_escape_equiv(unsigned); -JSONSL_API jsonsl_t jsonsl_new(int nlevels) { unsigned int ii; @@ -111,7 +39,7 @@ jsonsl_t jsonsl_new(int nlevels) jsn = (struct jsonsl_st *) bson_malloc0(sizeof (*jsn) + - ( (nlevels-1) * sizeof (struct jsonsl_state_st) ) + ( (nlevels) * sizeof (struct jsonsl_state_st) ) ); jsn->levels_max = (unsigned int) nlevels; @@ -123,7 +51,6 @@ jsonsl_t jsonsl_new(int nlevels) return jsn; } -JSONSL_API void jsonsl_reset(jsonsl_t jsn) { jsn->tok_last = 0; @@ -135,7 +62,6 @@ void jsonsl_reset(jsonsl_t jsn) jsn->expecting = 0; } -JSONSL_API void jsonsl_destroy(jsonsl_t jsn) { if (jsn) { @@ -170,8 +96,6 @@ jsonsl__str_fastparse(jsonsl_t jsn, *bytes >= 0x100 || #endif /* JSONSL_USE_WCHAR */ (is_simple_char(*bytes))) { - INCR_METRIC(TOTAL); - INCR_METRIC(STRINGY_INSIGNIFICANT); } else { /* Once we're done here, re-calculate the position variables */ jsn->pos += (bytes - *bytes_p); @@ -200,8 +124,6 @@ jsonsl__num_fastparse(jsonsl_t jsn, for (; nbytes; nbytes--, bytes++) { jsonsl_uchar_t c = *bytes; if (isdigit(c)) { - INCR_METRIC(TOTAL); - INCR_METRIC(NUMBER_FASTPATH); state->nelem = (state->nelem * 10) + (c - 0x30); } else { exhausted = 0; @@ -217,49 +139,49 @@ jsonsl__num_fastparse(jsonsl_t jsn, return FASTPARSE_BREAK; } -JSONSL_API void jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) { #define INVOKE_ERROR(eb) \ - if (jsn->error_callback(jsn, JSONSL_ERROR_##eb, state, (char*)c)) { \ - goto GT_AGAIN; \ - } \ - return; - -#define STACK_PUSH \ - if (jsn->level >= (levels_max-1)) { \ - jsn->error_callback(jsn, JSONSL_ERROR_LEVELS_EXCEEDED, state, (char*)c); \ + if (1) { \ + if (jsn->error_callback(jsn, JSONSL_ERROR_##eb, state, (char*)c)) { \ + goto GT_AGAIN; \ + } \ return; \ - } \ - state = jsn->stack + (++jsn->level); \ - state->ignore_callback = jsn->stack[jsn->level-1].ignore_callback; \ - state->pos_begin = jsn->pos; - -#define STACK_POP_NOPOS \ - state->pos_cur = jsn->pos; \ - state = jsn->stack + (--jsn->level); + } else ((void)0) - -#define STACK_POP \ - STACK_POP_NOPOS; \ - state->pos_cur = jsn->pos; +#define STACK_PUSH \ + if (1) { \ + if (jsn->level >= (levels_max-1)) { \ + jsn->error_callback(jsn, JSONSL_ERROR_LEVELS_EXCEEDED, state, (char*)c); \ + return; \ + } \ + state = jsn->stack + (++jsn->level); \ + state->ignore_callback = jsn->stack[jsn->level-1].ignore_callback; \ + state->pos_begin = jsn->pos; \ + } else ((void)0) #define CALLBACK_AND_POP_NOPOS(T) \ + if (1) { \ state->pos_cur = jsn->pos; \ DO_CALLBACK(T, POP); \ state->nescapes = 0; \ - state = jsn->stack + (--jsn->level); + state = jsn->stack + (--jsn->level); \ + } else ((void)0) #define CALLBACK_AND_POP(T) \ + if (1) { \ CALLBACK_AND_POP_NOPOS(T); \ - state->pos_cur = jsn->pos; + state->pos_cur = jsn->pos; \ + } else ((void)0) #define SPECIAL_POP \ - CALLBACK_AND_POP(SPECIAL); \ - jsn->expecting = 0; \ - jsn->tok_last = 0; \ + if (1) { \ + CALLBACK_AND_POP(SPECIAL); \ + jsn->expecting = 0; \ + jsn->tok_last = 0; \ + } else ((void)0) #define CUR_CHAR (*(jsonsl_uchar_t*)c) @@ -274,7 +196,7 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) jsn->action_callback(jsn, JSONSL_ACTION_##action, state, (jsonsl_char_t*)c); \ } \ if (jsn->stopfl) { return; } \ - } + } else ((void)0) /** * Verifies that we are able to insert the (non-string) item into a hash. @@ -282,19 +204,19 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) #define ENSURE_HVAL \ if (state->nelem % 2 == 0 && state->type == JSONSL_T_OBJECT) { \ INVOKE_ERROR(HKEY_EXPECTED); \ - } + } else ((void)0) #define VERIFY_SPECIAL(lit, lit_len) \ - if ((jsn->pos - state->pos_begin) > lit_len \ - || CUR_CHAR != (lit)[jsn->pos - state->pos_begin]) { \ - INVOKE_ERROR(SPECIAL_EXPECTED); \ - } + if ((jsn->pos - state->pos_begin) > lit_len \ + || CUR_CHAR != (lit)[jsn->pos - state->pos_begin]) { \ + INVOKE_ERROR(SPECIAL_EXPECTED); \ + } else ((void)0) #define VERIFY_SPECIAL_CI(lit, lit_len) \ - if ((jsn->pos - state->pos_begin) > lit_len \ - || tolower(CUR_CHAR) != (lit)[jsn->pos - state->pos_begin]) { \ - INVOKE_ERROR(SPECIAL_EXPECTED); \ - } + if ((jsn->pos - state->pos_begin) > lit_len \ + || tolower(CUR_CHAR) != (lit)[jsn->pos - state->pos_begin]) { \ + INVOKE_ERROR(SPECIAL_EXPECTED); \ + } else ((void)0) #define STATE_SPECIAL_LENGTH \ (state)->nescapes @@ -314,7 +236,6 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) for (; nbytes; nbytes--, jsn->pos++, c++) { unsigned state_type; - INCR_METRIC(TOTAL); GT_AGAIN: state_type = state->type; @@ -347,7 +268,6 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) INVOKE_ERROR(WEIRD_WHITESPACE); } } - INCR_METRIC(STRINGY_SLOWPATH); } else if (state_type == JSONSL_T_SPECIAL) { /* Fast track for signed/unsigned */ @@ -476,7 +396,6 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) } #endif } - INCR_METRIC(SPECIAL_FASTPATH); CONTINUE_NEXT_CHAR(); } @@ -529,7 +448,6 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) */ goto GT_STRUCTURAL_TOKEN; } else if (is_allowed_whitespace(CUR_CHAR)) { - INCR_METRIC(ALLOWED_WHITESPACE); /* So we're not special. Harmless insignificant whitespace * passthrough */ @@ -539,8 +457,6 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) goto GT_SPECIAL_BEGIN; } - INCR_GENERIC(CUR_CHAR); - if (CUR_CHAR == '"') { GT_QUOTE: jsn->can_insert = 0; @@ -601,7 +517,6 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) } /* switch(state->type) */ } else if (CUR_CHAR == '\\') { GT_ESCAPE: - INCR_METRIC(ESCAPES); /* Escape */ if ( (state->type & JSONSL_Tf_STRINGY) == 0 ) { INVOKE_ERROR(ESCAPE_OUTSIDE_STRING); @@ -614,7 +529,6 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) GT_STRUCTURAL_TOKEN: switch (CUR_CHAR) { case ':': - INCR_METRIC(STRUCTURAL_TOKEN); if (jsn->expecting != CUR_CHAR) { INVOKE_ERROR(STRAY_TOKEN); } @@ -624,7 +538,6 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) CONTINUE_NEXT_CHAR(); case ',': - INCR_METRIC(STRUCTURAL_TOKEN); /** * The comma is one of the more generic tokens. * In the context of an OBJECT, the can_insert flag @@ -653,7 +566,6 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) /* hashes are more common */ case '{': case '[': - INCR_METRIC(STRUCTURAL_TOKEN); if (!jsn->can_insert) { INVOKE_ERROR(CANT_INSERT); } @@ -681,7 +593,6 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) /* closing of list or object */ case '}': case ']': - INCR_METRIC(STRUCTURAL_TOKEN); if (jsn->tok_last == ',' && jsn->options.allow_trailing_comma == 0) { INVOKE_ERROR(TRAILING_COMMA); } @@ -754,7 +665,6 @@ jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) } } -JSONSL_API const char* jsonsl_strerror(jsonsl_error_t err) { if (err == JSONSL_ERROR_SUCCESS) { @@ -763,12 +673,11 @@ const char* jsonsl_strerror(jsonsl_error_t err) #define X(t) \ if (err == JSONSL_ERROR_##t) \ return #t; - JSONSL_XERR; + JSONSL_XERR #undef X return ""; } -JSONSL_API const char *jsonsl_strtype(jsonsl_type_t type) { #define X(o,c) \ @@ -786,7 +695,6 @@ const char *jsonsl_strtype(jsonsl_type_t type) * * */ -#ifndef JSONSL_NO_JPR static jsonsl_jpr_type_t populate_component(char *in, @@ -879,7 +787,6 @@ populate_component(char *in, return ret; } -JSONSL_API jsonsl_jpr_t jsonsl_jpr_new(const char *path, jsonsl_error_t *errp) { @@ -890,7 +797,7 @@ jsonsl_jpr_new(const char *path, jsonsl_error_t *errp) size_t origlen; jsonsl_error_t errstacked; -#define JPR_BAIL(err) *errp = err; goto GT_ERROR; +#define JPR_BAIL(err) if (1) { *errp = err; goto GT_ERROR; } else ((void)0) if (errp == NULL) { errp = &errstacked; @@ -1030,7 +937,6 @@ jsonsl__match_continue(jsonsl_jpr_t jpr, } } -JSONSL_API jsonsl_jpr_match_t jsonsl_path_match(jsonsl_jpr_t jpr, const struct jsonsl_state_st *parent, @@ -1061,7 +967,6 @@ jsonsl_path_match(jsonsl_jpr_t jpr, return jsonsl__match_continue(jpr, comp, parent->level, child->type); } -JSONSL_API jsonsl_jpr_match_t jsonsl_jpr_match(jsonsl_jpr_t jpr, unsigned int parent_type, @@ -1139,7 +1044,6 @@ jsonsl_jpr_match(jsonsl_jpr_t jpr, return JSONSL_MATCH_NOMATCH; } -JSONSL_API void jsonsl_jpr_match_state_init(jsonsl_t jsn, jsonsl_jpr_t *jprs, size_t njprs) @@ -1148,9 +1052,9 @@ void jsonsl_jpr_match_state_init(jsonsl_t jsn, if (njprs == 0) { return; } - jsn->jprs = (jsonsl_jpr_t *)malloc(sizeof(jsonsl_jpr_t) * njprs); + jsn->jprs = BSON_ARRAY_ALLOC(njprs, jsonsl_jpr_t); jsn->jpr_count = njprs; - jsn->jpr_root = (size_t*)calloc(1, sizeof(size_t) * njprs * jsn->levels_max); + jsn->jpr_root = BSON_ARRAY_ALLOC0(njprs * jsn->levels_max, size_t); memcpy(jsn->jprs, jprs, sizeof(jsonsl_jpr_t) * njprs); /* Set the initial jump table values */ @@ -1160,15 +1064,14 @@ void jsonsl_jpr_match_state_init(jsonsl_t jsn, } } -JSONSL_API void jsonsl_jpr_match_state_cleanup(jsonsl_t jsn) { if (jsn->jpr_count == 0) { return; } - free(jsn->jpr_root); - free(jsn->jprs); + bson_free(jsn->jpr_root); + bson_free(jsn->jprs); jsn->jprs = NULL; jsn->jpr_root = NULL; jsn->jpr_count = 0; @@ -1187,7 +1090,6 @@ void jsonsl_jpr_match_state_cleanup(jsonsl_t jsn) * main lexer itself. * */ -JSONSL_API jsonsl_jpr_t jsonsl_jpr_match_state(jsonsl_t jsn, struct jsonsl_state_st *state, const char *key, @@ -1252,7 +1154,6 @@ jsonsl_jpr_t jsonsl_jpr_match_state(jsonsl_t jsn, return NULL; } -JSONSL_API const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match) { #define X(T,v) \ @@ -1263,12 +1164,10 @@ const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match) return ""; } -#endif /* JSONSL_WITH_JPR */ - static char * jsonsl__writeutf8(uint32_t pt, char *out) { - #define ADD_OUTPUT(c) *out = (char)(c); out++; + #define ADD_OUTPUT(c) if (1) { *out = (char)(c); out++; } else ((void)0) if (pt < 0x80) { ADD_OUTPUT(pt); @@ -1315,9 +1214,11 @@ jsonsl__get_uescape_16(const char *s) int cur; #define GET_DIGIT(off) \ - cur = jsonsl__digit2int(s[off]); \ - if (cur == -1) { return -1; } \ - ret |= (cur << (12 - (off * 4))); + if (1) { \ + cur = jsonsl__digit2int(s[off]); \ + if (cur == -1) { return -1; } \ + ret |= (cur << (12 - (off * 4))); \ + } else ((void)0) GET_DIGIT(0); GET_DIGIT(1); @@ -1330,7 +1231,6 @@ jsonsl__get_uescape_16(const char *s) /** * Utility function to convert escape sequences */ -JSONSL_API size_t jsonsl_util_unescape_ex(const char *in, char *out, size_t len, @@ -1350,11 +1250,13 @@ size_t jsonsl_util_unescape_ex(const char *in, *oflags = 0; #define UNESCAPE_BAIL(e,offset) \ - *err = JSONSL_ERROR_##e; \ - if (errat) { \ - *errat = (const char*)(c+ (ptrdiff_t)(offset)); \ - } \ - return 0; + if (1) { \ + *err = JSONSL_ERROR_##e; \ + if (errat) { \ + *errat = (const char*)(c+ (ptrdiff_t)(offset)); \ + } \ + return 0; \ + } else ((void)0) for (; len; len--, c++, out++) { int uescval; @@ -1367,7 +1269,7 @@ size_t jsonsl_util_unescape_ex(const char *in, UNESCAPE_BAIL(ESCAPE_INVALID, 0); } if (!is_allowed_escape(c[1])) { - UNESCAPE_BAIL(ESCAPE_INVALID, 1) + UNESCAPE_BAIL(ESCAPE_INVALID, 1); } if ((toEscape && toEscape[(unsigned char)c[1] & 0x7f] == 0 && c[1] != '\\' && c[1] != '"')) { @@ -1658,14 +1560,9 @@ static int is_simple_char(unsigned c) { } /* Clean up all our macros! */ -#undef INCR_METRIC -#undef INCR_GENERIC -#undef INCR_STRINGY_CATCH #undef CASE_DIGITS #undef INVOKE_ERROR #undef STACK_PUSH -#undef STACK_POP_NOPOS -#undef STACK_POP #undef CALLBACK_AND_POP_NOPOS #undef CALLBACK_AND_POP #undef SPECIAL_POP diff --git a/bsonjs/jsonsl/jsonsl.h b/bsonjs/jsonsl/jsonsl.h index d4d9832..e605867 100644 --- a/bsonjs/jsonsl/jsonsl.h +++ b/bsonjs/jsonsl/jsonsl.h @@ -9,11 +9,14 @@ * See included LICENSE file for license details. */ -#include "../bson/bson-prelude.h" - #ifndef JSONSL_H_ #define JSONSL_H_ +#include +#include + +#include + #include #include #include @@ -73,19 +76,6 @@ typedef int ssize_t; #define JSONSL_JPR_COMPONENT_USER_FIELDS #endif -#ifndef JSONSL_API -/** - * We require a /DJSONSL_DLL so that users already using this as a static - * or embedded library don't get confused - */ -#if defined(_WIN32) && defined(JSONSL_DLL) -#define JSONSL_API __declspec(dllexport) -#else -#define JSONSL_API -#endif /* _WIN32 */ - -#endif /* !JSONSL_API */ - #ifndef JSONSL_INLINE #if defined(_MSC_VER) #define JSONSL_INLINE __inline @@ -554,21 +544,28 @@ struct jsonsl_st { int can_insert; unsigned int levels_max; -#ifndef JSONSL_NO_JPR size_t jpr_count; jsonsl_jpr_t *jprs; /* Root pointer for JPR matching information */ size_t *jpr_root; -#endif /* JSONSL_NO_JPR */ /*@}*/ +#if defined(_MSC_VER) +#pragma warning (push) +#pragma warning (disable : 4200) +#endif // defined(_MSC_VER) + /** * This is the stack. Its upper bound is levels_max, or the * nlevels argument passed to jsonsl_new. If you modify this structure, * make sure that this member is last. */ - struct jsonsl_state_st stack[1]; + struct jsonsl_state_st stack[]; + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif // defined(_MSC_VER) }; @@ -577,7 +574,6 @@ struct jsonsl_st { * * @param nlevels maximum recursion depth */ -JSONSL_API jsonsl_t jsonsl_new(int nlevels); /** @@ -587,7 +583,6 @@ jsonsl_t jsonsl_new(int nlevels); * @param bytes new data to be fed * @param nbytes size of new data */ -JSONSL_API void jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes); /** @@ -597,7 +592,6 @@ void jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes); * * @param jsn the lexer */ -JSONSL_API void jsonsl_reset(jsonsl_t jsn); /** @@ -605,7 +599,6 @@ void jsonsl_reset(jsonsl_t jsn); * * @param jsn the lexer */ -JSONSL_API void jsonsl_destroy(jsonsl_t jsn); /** @@ -675,21 +668,9 @@ void jsonsl_enable_all_callbacks(jsonsl_t jsn) * of the error or type, respectively. They will never * return NULL */ -JSONSL_API const char* jsonsl_strerror(jsonsl_error_t err); -JSONSL_API const char* jsonsl_strtype(jsonsl_type_t jt); -/** - * Dumps global metrics to the screen. This is a noop unless - * jsonsl was compiled with JSONSL_USE_METRICS - */ -JSONSL_API -void jsonsl_dump_global_metrics(void); - -/* This macro just here for editors to do code folding */ -#ifndef JSONSL_NO_JPR - /** * @name JSON Pointer API * @@ -803,13 +784,11 @@ struct jsonsl_jpr_st { * * @return a new jsonsl_jpr_t object, or NULL on error. */ -JSONSL_API jsonsl_jpr_t jsonsl_jpr_new(const char *path, jsonsl_error_t *errp); /** * Destroy a JPR object */ -JSONSL_API void jsonsl_jpr_destroy(jsonsl_jpr_t jpr); /** @@ -830,7 +809,6 @@ void jsonsl_jpr_destroy(jsonsl_jpr_t jpr); * @return a status constant. This indicates whether a match was excluded, possible, * or successful. */ -JSONSL_API jsonsl_jpr_match_t jsonsl_jpr_match(jsonsl_jpr_t jpr, unsigned int parent_type, unsigned int parent_level, @@ -861,7 +839,6 @@ jsonsl_jpr_match_t jsonsl_jpr_match(jsonsl_jpr_t jpr, * Since this function also checks the state of the child, it should only * be called on PUSH callbacks, and not POP callbacks */ -JSONSL_API jsonsl_jpr_match_t jsonsl_path_match(jsonsl_jpr_t jpr, const struct jsonsl_state_st *parent, @@ -885,7 +862,6 @@ jsonsl_path_match(jsonsl_jpr_t jpr, * @param jprs An array of jsonsl_jpr_t objects * @param njprs How many elements in the jprs array. */ -JSONSL_API void jsonsl_jpr_match_state_init(jsonsl_t jsn, jsonsl_jpr_t *jprs, size_t njprs); @@ -910,7 +886,6 @@ void jsonsl_jpr_match_state_init(jsonsl_t jsn, * does not mean matching has failed, it can still be part of the match: check * the out parameter). */ -JSONSL_API jsonsl_jpr_t jsonsl_jpr_match_state(jsonsl_t jsn, struct jsonsl_state_st *state, const char *key, @@ -923,13 +898,11 @@ jsonsl_jpr_t jsonsl_jpr_match_state(jsonsl_t jsn, * match_state_init() and match_state() * @param jsn The lexer */ -JSONSL_API void jsonsl_jpr_match_state_cleanup(jsonsl_t jsn); /** * Return a string representation of the match result returned by match() */ -JSONSL_API const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match); /* @}*/ @@ -982,7 +955,6 @@ const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match); * (i.e. \uXXXX) will occupy six bytes in the source, but at the most * two bytes when escaped. */ -JSONSL_API size_t jsonsl_util_unescape_ex(const char *in, char *out, size_t len, @@ -997,8 +969,6 @@ size_t jsonsl_util_unescape_ex(const char *in, #define jsonsl_util_unescape(in, out, len, toEscape, err) \ jsonsl_util_unescape_ex(in, out, len, toEscape, NULL, err, NULL) -#endif /* JSONSL_NO_JPR */ - #ifdef __cplusplus } #endif /* __cplusplus */ From c5a0f14f1b6280b83f5cb63c7439009dc2d539b7 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 1 Sep 2026 06:55:34 -0500 Subject: [PATCH 03/69] Fix vendor.sh for libbson 2.x's common/src and mlib layout --- vendor.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vendor.sh b/vendor.sh index 5e44c20..bad167c 100644 --- a/vendor.sh +++ b/vendor.sh @@ -5,9 +5,8 @@ rm -rf mongo-c-driver git clone git@github.com:mongodb/mongo-c-driver.git pushd mongo-c-driver git checkout $VERSION -python build/calc_release_version.py > VERSION_CURRENT mkdir cmake-build && cd cmake-build -cmake -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF -DENABLE_MONGOC=OFF .. +cmake -DENABLE_MONGOC=OFF .. popd rm -r bsonjs/bson rm -r bsonjs/jsonsl @@ -16,8 +15,10 @@ rsync -r mongo-c-driver/src/libbson/src/bson/*.[hc] bsonjs/bson/ rsync -r mongo-c-driver/src/libbson/src/jsonsl/*.[hc] bsonjs/jsonsl/ rsync -r mongo-c-driver/src/libbson/src/jsonsl/LICENSE bsonjs/jsonsl/ -rsync -r mongo-c-driver/src/common/*.[hc] bsonjs/common/ -rsync -r mongo-c-driver/cmake-build/src/common/*.[hc] bsonjs/common/ +rsync -r mongo-c-driver/src/common/src/*.[hc] bsonjs/common/ +rsync -r mongo-c-driver/cmake-build/src/common/src/*.[hc] bsonjs/common/ +mkdir -p bsonjs/common/mlib +rsync -r mongo-c-driver/src/common/src/mlib/*.h mongo-c-driver/src/common/src/mlib/*.th bsonjs/common/mlib/ rsync -r mongo-c-driver/cmake-build/src/libbson/src/bson/*.[hc] bsonjs/bson/ From 522e816a544cc20160be8182331535640ada12f0 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 1 Sep 2026 06:55:40 -0500 Subject: [PATCH 04/69] Fix build against vendored libbson 2.5.0 libbson 2.x's mlib/time_point.h needs POSIX feature-test macros defined directly rather than detected, and bson_as_json() was removed in favor of bson_as_json_with_opts(). --- bsonjs/bsonjs.c | 4 +++- setup.py | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/bsonjs/bsonjs.c b/bsonjs/bsonjs.c index 29398fd..bbfb347 100644 --- a/bsonjs/bsonjs.c +++ b/bsonjs/bsonjs.c @@ -45,7 +45,9 @@ int mode) } else if (mode == 2) { json = bson_as_canonical_extended_json(b, json_len); } else if (mode == 0) { - json = bson_as_json(b, json_len); + bson_json_opts_t *opts = bson_json_opts_new(BSON_JSON_MODE_LEGACY, -1); + json = bson_as_json_with_opts(b, json_len, opts); + bson_json_opts_destroy(opts); } else { PyErr_SetString(PyExc_ValueError, "The value of mode must be one of: " "bsonjs.RELAXED, bsonjs.LEGACY, " diff --git a/setup.py b/setup.py index c16fc3e..d7a1820 100644 --- a/setup.py +++ b/setup.py @@ -18,11 +18,21 @@ from setuptools import setup, Extension libraries = [] +define_macros = [("BSON_COMPILATION", 1), + ("Py_LIMITED_API", "0x03090000")] if sys.platform == "win32": libraries.append("ws2_32") -elif sys.platform != "darwin": - # librt may be needed for clock_gettime() - libraries.append("rt") +else: + # libbson's mlib/time_point.h needs these feature test macros to see + # POSIX clock functions (clock_gettime, etc.). + define_macros += [("_XOPEN_SOURCE", "700"), + ("_BSD_SOURCE", 1), + ("_DEFAULT_SOURCE", 1)] + if sys.platform == "darwin": + define_macros.append(("_DARWIN_C_SOURCE", 1)) + else: + # librt may be needed for clock_gettime() + libraries.append("rt") setup( ext_modules=[ @@ -34,8 +44,7 @@ "bsonjs/jsonsl", "bsonjs/common"], py_limited_api=True, - define_macros=[("BSON_COMPILATION", 1), - ("Py_LIMITED_API", "0x03090000")], + define_macros=define_macros, libraries=libraries ) ], From 658a703c366fbbe068c0167093ff7e87b7cba76c Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 1 Sep 2026 06:55:46 -0500 Subject: [PATCH 05/69] Update docs for libbson 2.5.0 bump --- CHANGELOG.rst | 7 +++++++ README.rst | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fa098c0..c50f2fa 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,13 @@ Changelog ========= +0.8.0 +````` +Version 0.8.0 updates python-bsonjs's vendored copy of libbson to 2.5.0. +For a detailed breakdown of what changed in each version of libbson see its changelog: +https://github.com/mongodb/mongo-c-driver/blob/2.5.0/NEWS +http://mongoc.org/libbson/2.5.0/ + 0.7.0 ````` - Add support for Python 3.14. diff --git a/README.rst b/README.rst index 6feb7e2..d6e3ad3 100644 --- a/README.rst +++ b/README.rst @@ -9,7 +9,7 @@ About ===== A fast BSON to MongoDB Extended JSON converter for Python that uses -`libbson `_. +`libbson `_. Installation ============ From 102e30c7d8b1c2e608e706bdf169f64315551906 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 1 Sep 2026 08:37:06 -0500 Subject: [PATCH 06/69] Update README speed claim for libbson 2.5.0 --- README.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index d6e3ad3..467744b 100644 --- a/README.rst +++ b/README.rst @@ -55,21 +55,21 @@ Using bsonjs with pymongo to insert a RawBSONDocument. Speed ===== -bsonjs is roughly 3-4x faster than PyMongo's json_util at decoding BSON to +bsonjs is roughly 5-17x faster than PyMongo's json_util at decoding BSON to JSON and encoding JSON to BSON. See `benchmark.py`:: $ python benchmark.py Timing: bsonjs.dumps(b) - 10000 loops, best of 3: 0.04682216700166464 + 10000 loops, best of 3: 0.028451045000110753 Timing: json_util.dumps(bson.decode(b)) - 10000 loops, best of 3: 0.17319270805455744 - bsonjs is 3.70x faster than json_util + 10000 loops, best of 3: 0.47029594800005725 + bsonjs is 16.53x faster than json_util Timing: bsonjs.loads(j) - 10000 loops, best of 3: 0.053156834095716476 + 10000 loops, best of 3: 0.08982307400015088 Timing: bson.encode(json_util.loads(j)) - 10000 loops, best of 3: 0.15982166700996459 - bsonjs is 3.01x faster than json_util + 10000 loops, best of 3: 0.4777698939999482 + bsonjs is 5.32x faster than json_util Limitations From f1221dc340369888568967da10656882d9b9b1d8 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 3 Sep 2026 04:51:52 -0500 Subject: [PATCH 07/69] Address code review feedback on the libbson 2.5.0 bump - vendor.sh now diffs and reverts bsonjs/common/common-config.h the same way it already does for bsonjs/bson/config.h, instead of silently overwriting the hand-checked-in copy. - MANIFEST.in now includes *.th files so the new mlib/vec.th template ships in the sdist. - bsonjs.c uses bson_as_legacy_extended_json() instead of manually building bson_json_opts_t, matching the RELAXED/CANONICAL calls already in this function. --- MANIFEST.in | 1 + bsonjs/bsonjs.c | 4 +--- vendor.sh | 6 +++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 5531919..3e663bb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,6 +3,7 @@ include CHANGELOG.rst include LICENSE recursive-include bsonjs LICENSE recursive-include bsonjs *.h +recursive-include bsonjs *.th recursive-include bsonjs *.py exclude benchmark.py exclude build-wheels.sh diff --git a/bsonjs/bsonjs.c b/bsonjs/bsonjs.c index bbfb347..774922f 100644 --- a/bsonjs/bsonjs.c +++ b/bsonjs/bsonjs.c @@ -45,9 +45,7 @@ int mode) } else if (mode == 2) { json = bson_as_canonical_extended_json(b, json_len); } else if (mode == 0) { - bson_json_opts_t *opts = bson_json_opts_new(BSON_JSON_MODE_LEGACY, -1); - json = bson_as_json_with_opts(b, json_len, opts); - bson_json_opts_destroy(opts); + json = bson_as_legacy_extended_json(b, json_len); } else { PyErr_SetString(PyExc_ValueError, "The value of mode must be one of: " "bsonjs.RELAXED, bsonjs.LEGACY, " diff --git a/vendor.sh b/vendor.sh index bad167c..c6cc053 100644 --- a/vendor.sh +++ b/vendor.sh @@ -22,7 +22,11 @@ rsync -r mongo-c-driver/src/common/src/mlib/*.h mongo-c-driver/src/common/src/ml rsync -r mongo-c-driver/cmake-build/src/libbson/src/bson/*.[hc] bsonjs/bson/ -# Ignore autogenerated config.h +# Ignore autogenerated config.h and common-config.h git diff -- bsonjs/bson/config.h | tee echo "**** Review libbson's autogenerated src/bson/config.h (above) for newly added (or removed) macros ****" git checkout -- bsonjs/bson/config.h + +git diff -- bsonjs/common/common-config.h | tee +echo "**** Review libbson's autogenerated src/common/common-config.h (above) for newly added (or removed) macros ****" +git checkout -- bsonjs/common/common-config.h From 3bb2f63502dedf5d7f40ed3fd46c5148740e3462 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 4 Sep 2026 05:42:57 -0500 Subject: [PATCH 08/69] Add CodeQL analysis workflow Uses drivers-github-tools' reusable codeql action, scanning our own C code (bsonjs.c/bsonjs.h), the Python build/test glue, and the workflow files themselves. Excludes bsonjs/{bson,jsonsl,common}, which are vendored libbson/jsonsl sources synced by vendor.sh rather than code we maintain. --- .github/workflows/codeql.yml | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..b945019 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,46 @@ +name: "CodeQL" + +on: + push: + branches: ["main"] + pull_request: + schedule: + - cron: '17 10 * * 2' + +concurrency: + group: codeql-${{ github.ref }} + cancel-in-progress: true + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + timeout-minutes: 360 + permissions: + security-events: write + + strategy: + fail-fast: false + matrix: + include: + - language: c-cpp + build-mode: manual + manual-build-command: pip install -e . + - language: python + build-mode: none + - language: actions + build-mode: none + steps: + - uses: mongodb-labs/drivers-github-tools/codeql@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3 + with: + language: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + manual-build-command: ${{ matrix.manual-build-command }} + config: | + paths-ignore: + # Vendored libbson/jsonsl sources, mechanically synced by + # vendor.sh — not ours to fix findings in. + - 'bsonjs/bson/**' + - 'bsonjs/jsonsl/**' + - 'bsonjs/common/**' + - 'test/**' From 112925301b73eaa75f3e0560b18b90979ac0ff84 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 4 Sep 2026 05:52:33 -0500 Subject: [PATCH 09/69] Add .codeqlignore to exclude vendored sources from CodeQL The workflow's paths-ignore config didn't keep two alerts in vendored libbson files (bsonjs/bson/bson-decimal128.c, bson-timegm.c) out of the c-cpp analysis, despite those paths being listed there and confirmed present in CodeQL's own augmented config. .codeqlignore is applied before extraction and is the mechanism GitHub documents as reliable for compiled languages. --- .codeqlignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .codeqlignore diff --git a/.codeqlignore b/.codeqlignore new file mode 100644 index 0000000..637df56 --- /dev/null +++ b/.codeqlignore @@ -0,0 +1,6 @@ +# Vendored libbson/jsonsl sources, mechanically synced by vendor.sh — +# not ours to fix CodeQL findings in. +bsonjs/bson/ +bsonjs/jsonsl/ +bsonjs/common/ +test/ From 9e2cd0cbf881c0a5134551585e03a99f72ebd77d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 4 Sep 2026 06:00:52 -0500 Subject: [PATCH 10/69] Exclude vendored libbson/jsonsl sources from CodeQL by never compiling them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit paths-ignore only filters results for interpreted languages or build-mode: none — it doesn't apply when build-mode is manual/autobuild, per GitHub's own docs, so it never excluded bsonjs/bson/*.c from the c-cpp analysis despite listing them. .codeqlignore is likewise a no-op here since it's only read by GitHub's Default Setup, which this repo doesn't use. CodeQL only extracts files the compiler is actually invoked on, so the c-cpp job now compiles just bsonjs.c directly instead of running the full `pip install -e .`, which also built every vendored .c file under bsonjs/{bson,jsonsl,common}. --- .codeqlignore | 6 ------ .github/workflows/codeql.yml | 14 +++++++++++++- 2 files changed, 13 insertions(+), 7 deletions(-) delete mode 100644 .codeqlignore diff --git a/.codeqlignore b/.codeqlignore deleted file mode 100644 index 637df56..0000000 --- a/.codeqlignore +++ /dev/null @@ -1,6 +0,0 @@ -# Vendored libbson/jsonsl sources, mechanically synced by vendor.sh — -# not ours to fix CodeQL findings in. -bsonjs/bson/ -bsonjs/jsonsl/ -bsonjs/common/ -test/ diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b945019..d2300c2 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -25,7 +25,19 @@ jobs: include: - language: c-cpp build-mode: manual - manual-build-command: pip install -e . + # Compile only our own bsonjs.c, not the vendored libbson/jsonsl + # sources under bsonjs/{bson,jsonsl,common}. CodeQL's tracer + # only extracts files the compiler is actually invoked on, and + # for build-mode: manual, paths-ignore below does not filter + # results (that only works for interpreted languages or + # build-mode: none) — so keeping vendored code out of scope + # means never compiling it here. + manual-build-command: | + PYINC=$(python3 -c "import sysconfig; print(sysconfig.get_paths()['include'])") + gcc -c bsonjs/bsonjs.c -o /tmp/bsonjs.o \ + -Ibsonjs -Ibsonjs/bson -Ibsonjs/jsonsl -Ibsonjs/common -I"$PYINC" \ + -DBSON_COMPILATION=1 -DPy_LIMITED_API=0x03090000 \ + -D_XOPEN_SOURCE=700 -D_BSD_SOURCE=1 -D_DEFAULT_SOURCE=1 - language: python build-mode: none - language: actions From 138b1ba02f14ee1636fd57be9fef26f0e10d5262 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 4 Sep 2026 06:26:38 -0500 Subject: [PATCH 11/69] Update CodeQL workflow permissions and timeout Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/codeql.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d2300c2..f874789 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -15,11 +15,10 @@ jobs: analyze: name: Analyze (${{ matrix.language }}) runs-on: ubuntu-latest - timeout-minutes: 360 permissions: security-events: write - - strategy: + contents: read + actions: read fail-fast: false matrix: include: From 85f540f89e367a0525a0663991396e2efb9f8931 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 4 Sep 2026 06:43:03 -0500 Subject: [PATCH 12/69] Fix invalid CodeQL workflow YAML fail-fast and matrix had ended up nested under permissions instead of strategy, so the workflow failed to parse. --- .github/workflows/codeql.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index f874789..6805691 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -19,6 +19,8 @@ jobs: security-events: write contents: read actions: read + + strategy: fail-fast: false matrix: include: From cfcaaea641aad81219b530a66c1fb7c354ded039 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 4 Sep 2026 06:58:28 -0500 Subject: [PATCH 13/69] Drop unused vendored sources from the build bson-bcon.c (BCON), common-md5.c, and common-thread.c implement libbson features bsonjs never calls, and nothing else in the vendored tree references their symbols either. Verified via nm against the rest of the built extension and confirmed by import + the test suite. bson-vector.c looked like another candidate by the same criteria, but bson.c's array builder actually calls into it (bson_append_array_from_vector), so it stays. --- setup.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d7a1820..a246fa6 100644 --- a/setup.py +++ b/setup.py @@ -13,10 +13,20 @@ # limitations under the License. import glob +import os import sys from setuptools import setup, Extension +# Vendored libbson sources for features bsonjs doesn't use (BCON, MD5, +# the thread helpers) and that nothing else in the vendored tree calls +# into, confirmed via nm against the rest of the built extension. +_UNUSED_VENDORED_SOURCES = frozenset(( + "bson-bcon.c", + "common-md5.c", + "common-thread.c", +)) + libraries = [] define_macros = [("BSON_COMPILATION", 1), ("Py_LIMITED_API", "0x03090000")] @@ -38,7 +48,10 @@ ext_modules=[ Extension( "bsonjs", - sources=["bsonjs/bsonjs.c"] + glob.glob("bsonjs/*/*.c"), + sources=["bsonjs/bsonjs.c"] + [ + src for src in glob.glob("bsonjs/*/*.c") + if os.path.basename(src) not in _UNUSED_VENDORED_SOURCES + ], include_dirs=["bsonjs", "bsonjs/bson", "bsonjs/jsonsl", From 5782ccebbb436aeccbda63decd6aa88b54d618b0 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 4 Sep 2026 08:22:42 -0500 Subject: [PATCH 14/69] Drop unused vendored sources at vendor time instead of build time vendor.sh now deletes bson-bcon.c, common-md5.c, and common-thread.c right after syncing them, so future vendor bumps keep them out automatically instead of needing setup.py to filter them out of the build every time. Their headers stay: bson.h includes bson-bcon.h, and common-thread-private.h is a real dependency of bson-context.c and common-b64.c. setup.py's sources glob goes back to unfiltered, now that the files it would have excluded no longer exist in the tree. --- bsonjs/bson/bson-bcon.c | 995 ---------------------------------- bsonjs/common/common-md5.c | 394 -------------- bsonjs/common/common-thread.c | 75 --- 3 files changed, 1464 deletions(-) delete mode 100644 bsonjs/bson/bson-bcon.c delete mode 100644 bsonjs/common/common-md5.c delete mode 100644 bsonjs/common/common-thread.c diff --git a/bsonjs/bson/bson-bcon.c b/bsonjs/bson/bson-bcon.c deleted file mode 100644 index 503a911..0000000 --- a/bsonjs/bson/bson-bcon.c +++ /dev/null @@ -1,995 +0,0 @@ -/* - * @file bcon.c - * @brief BCON (BSON C Object Notation) Implementation - */ - -/* Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include - -#include - -#include - -/* These stack manipulation macros are used to manage append recursion in - * bcon_append_ctx_va(). They take care of some awkward dereference rules (the - * real bson object isn't in the stack, but accessed by pointer) and add in run - * time asserts to make sure we don't blow the stack in either direction */ - -#define STACK_ELE(_delta, _name) (ctx->stack[(_delta) + ctx->n]._name) - -#define STACK_BSON(_delta) (((_delta) + ctx->n) == 0 ? bson : &STACK_ELE(_delta, bson)) - -#define STACK_ITER(_delta) (((_delta) + ctx->n) == 0 ? &root_iter : &STACK_ELE(_delta, iter)) - -#define STACK_BSON_PARENT STACK_BSON(-1) -#define STACK_BSON_CHILD STACK_BSON(0) - -#define STACK_ITER_CHILD STACK_ITER(0) - -#define STACK_I STACK_ELE(0, i) -#define STACK_IS_ARRAY STACK_ELE(0, is_array) - -#define STACK_PUSH_ARRAY(statement) \ - do { \ - BSON_ASSERT(ctx->n < (BCON_STACK_MAX - 1)); \ - ctx->n++; \ - STACK_I = 0; \ - STACK_IS_ARRAY = 1; \ - statement; \ - } while (0) - -#define STACK_PUSH_DOC(statement) \ - do { \ - BSON_ASSERT(ctx->n < (BCON_STACK_MAX - 1)); \ - ctx->n++; \ - STACK_IS_ARRAY = 0; \ - statement; \ - } while (0) - -#define STACK_POP_ARRAY(statement) \ - do { \ - BSON_ASSERT(STACK_IS_ARRAY); \ - BSON_ASSERT(ctx->n != 0); \ - statement; \ - ctx->n--; \ - } while (0) - -#define STACK_POP_DOC(statement) \ - do { \ - BSON_ASSERT(!STACK_IS_ARRAY); \ - BSON_ASSERT(ctx->n != 0); \ - statement; \ - ctx->n--; \ - } while (0) - -/* This is a landing pad union for all of the types we can process with bcon. - * We need actual storage for this to capture the return value of va_arg, which - * takes multiple calls to get everything we need for some complex types */ -typedef union bcon_append { - char *UTF8; - double DOUBLE; - bson_t *DOCUMENT; - bson_t *ARRAY; - bson_t *BCON; - - struct { - bson_subtype_t subtype; - uint8_t *binary; - uint32_t length; - } BIN; - - bson_oid_t *OID; - bool BOOL; - int64_t DATE_TIME; - - struct { - char *regex; - char *flags; - } REGEX; - - struct { - char *collection; - bson_oid_t *oid; - } DBPOINTER; - - const char *CODE; - - char *SYMBOL; - - struct { - const char *js; - bson_t *scope; - } CODEWSCOPE; - - int32_t INT32; - - struct { - uint32_t timestamp; - uint32_t increment; - } TIMESTAMP; - - int64_t INT64; - bson_decimal128_t *DECIMAL128; - const bson_iter_t *ITER; -} bcon_append_t; - -/* same as bcon_append_t. Some extra symbols and varying types that handle the - * differences between bson_append and bson_iter */ -typedef union bcon_extract { - bson_type_t TYPE; - bson_iter_t *ITER; - const char *key; - const char **UTF8; - double *DOUBLE; - bson_t *DOCUMENT; - bson_t *ARRAY; - - struct { - bson_subtype_t *subtype; - const uint8_t **binary; - uint32_t *length; - } BIN; - - const bson_oid_t **OID; - bool *BOOL; - int64_t *DATE_TIME; - - struct { - const char **regex; - const char **flags; - } REGEX; - - struct { - const char **collection; - const bson_oid_t **oid; - } DBPOINTER; - - const char **CODE; - - const char **SYMBOL; - - struct { - const char **js; - bson_t *scope; - } CODEWSCOPE; - - int32_t *INT32; - - struct { - uint32_t *timestamp; - uint32_t *increment; - } TIMESTAMP; - - int64_t *INT64; - bson_decimal128_t *DECIMAL128; -} bcon_extract_t; - -static const char *gBconMagic = "BCON_MAGIC"; -static const char *gBconeMagic = "BCONE_MAGIC"; - -const char * -bson_bcon_magic(void) -{ - return gBconMagic; -} - - -const char * -bson_bcone_magic(void) -{ - return gBconeMagic; -} - -static void -_noop(void) -{ -} - -/* appends val to the passed bson object. Meant to be a super simple dispatch - * table */ -static void -_bcon_append_single(bson_t *bson, bcon_type_t type, const char *key, bcon_append_t *val) -{ - switch ((int)type) { - case BCON_TYPE_UTF8: - BSON_ASSERT(bson_append_utf8(bson, key, -1, val->UTF8, -1)); - break; - case BCON_TYPE_DOUBLE: - BSON_ASSERT(bson_append_double(bson, key, -1, val->DOUBLE)); - break; - case BCON_TYPE_BIN: { - BSON_ASSERT(bson_append_binary(bson, key, -1, val->BIN.subtype, val->BIN.binary, val->BIN.length)); - break; - } - case BCON_TYPE_UNDEFINED: - BSON_ASSERT(bson_append_undefined(bson, key, -1)); - break; - case BCON_TYPE_OID: - BSON_ASSERT(bson_append_oid(bson, key, -1, val->OID)); - break; - case BCON_TYPE_BOOL: - BSON_ASSERT(bson_append_bool(bson, key, -1, (bool)val->BOOL)); - break; - case BCON_TYPE_DATE_TIME: - BSON_ASSERT(bson_append_date_time(bson, key, -1, val->DATE_TIME)); - break; - case BCON_TYPE_NULL: - BSON_ASSERT(bson_append_null(bson, key, -1)); - break; - case BCON_TYPE_REGEX: { - BSON_ASSERT(bson_append_regex(bson, key, -1, val->REGEX.regex, val->REGEX.flags)); - break; - } - case BCON_TYPE_DBPOINTER: { - BSON_ASSERT(bson_append_dbpointer(bson, key, -1, val->DBPOINTER.collection, val->DBPOINTER.oid)); - break; - } - case BCON_TYPE_CODE: - BSON_ASSERT(bson_append_code(bson, key, -1, val->CODE)); - break; - case BCON_TYPE_SYMBOL: - BSON_ASSERT(bson_append_symbol(bson, key, -1, val->SYMBOL, -1)); - break; - case BCON_TYPE_CODEWSCOPE: - BSON_ASSERT(bson_append_code_with_scope(bson, key, -1, val->CODEWSCOPE.js, val->CODEWSCOPE.scope)); - break; - case BCON_TYPE_INT32: - BSON_ASSERT(bson_append_int32(bson, key, -1, val->INT32)); - break; - case BCON_TYPE_TIMESTAMP: { - BSON_ASSERT(bson_append_timestamp(bson, key, -1, val->TIMESTAMP.timestamp, val->TIMESTAMP.increment)); - break; - } - case BCON_TYPE_INT64: - BSON_ASSERT(bson_append_int64(bson, key, -1, val->INT64)); - break; - case BCON_TYPE_DECIMAL128: - BSON_ASSERT(bson_append_decimal128(bson, key, -1, val->DECIMAL128)); - break; - case BCON_TYPE_MAXKEY: - BSON_ASSERT(bson_append_maxkey(bson, key, -1)); - break; - case BCON_TYPE_MINKEY: - BSON_ASSERT(bson_append_minkey(bson, key, -1)); - break; - case BCON_TYPE_ARRAY: { - BSON_ASSERT(bson_append_array(bson, key, -1, val->ARRAY)); - break; - } - case BCON_TYPE_DOCUMENT: { - BSON_ASSERT(bson_append_document(bson, key, -1, val->DOCUMENT)); - break; - } - case BCON_TYPE_ITER: - BSON_ASSERT(bson_append_iter(bson, key, -1, val->ITER)); - break; - default: - BSON_ASSERT(0); - break; - } -} - -#define CHECK_TYPE(_type) \ - do { \ - if (bson_iter_type(iter) != (_type)) { \ - return false; \ - } \ - } while (0) - -/* extracts the value under the iterator and writes it to val. returns false - * if the iterator type doesn't match the token type. - * - * There are two magic tokens: - * - * BCONE_SKIP - - * Let's us verify that a key has a type, without caring about its value. - * This allows for wider declarative BSON verification - * - * BCONE_ITER - - * Returns the underlying iterator. This could allow for more complicated, - * procedural verification (if a parameter could have multiple types). - * */ -static bool -_bcon_extract_single(const bson_iter_t *iter, bcon_type_t type, bcon_extract_t *val) -{ - switch ((int)type) { - case BCON_TYPE_UTF8: - CHECK_TYPE(BSON_TYPE_UTF8); - *val->UTF8 = bson_iter_utf8(iter, NULL); - break; - case BCON_TYPE_DOUBLE: - CHECK_TYPE(BSON_TYPE_DOUBLE); - *val->DOUBLE = bson_iter_double(iter); - break; - case BCON_TYPE_BIN: - CHECK_TYPE(BSON_TYPE_BINARY); - bson_iter_binary(iter, val->BIN.subtype, val->BIN.length, val->BIN.binary); - break; - case BCON_TYPE_UNDEFINED: - CHECK_TYPE(BSON_TYPE_UNDEFINED); - break; - case BCON_TYPE_OID: - CHECK_TYPE(BSON_TYPE_OID); - *val->OID = bson_iter_oid(iter); - break; - case BCON_TYPE_BOOL: - CHECK_TYPE(BSON_TYPE_BOOL); - *val->BOOL = bson_iter_bool(iter); - break; - case BCON_TYPE_DATE_TIME: - CHECK_TYPE(BSON_TYPE_DATE_TIME); - *val->DATE_TIME = bson_iter_date_time(iter); - break; - case BCON_TYPE_NULL: - CHECK_TYPE(BSON_TYPE_NULL); - break; - case BCON_TYPE_REGEX: - CHECK_TYPE(BSON_TYPE_REGEX); - *val->REGEX.regex = bson_iter_regex(iter, val->REGEX.flags); - - break; - case BCON_TYPE_DBPOINTER: - CHECK_TYPE(BSON_TYPE_DBPOINTER); - bson_iter_dbpointer(iter, NULL, val->DBPOINTER.collection, val->DBPOINTER.oid); - break; - case BCON_TYPE_CODE: - CHECK_TYPE(BSON_TYPE_CODE); - *val->CODE = bson_iter_code(iter, NULL); - break; - case BCON_TYPE_SYMBOL: - CHECK_TYPE(BSON_TYPE_SYMBOL); - *val->SYMBOL = bson_iter_symbol(iter, NULL); - break; - case BCON_TYPE_CODEWSCOPE: { - const uint8_t *buf; - uint32_t len; - - CHECK_TYPE(BSON_TYPE_CODEWSCOPE); - - *val->CODEWSCOPE.js = bson_iter_codewscope(iter, NULL, &len, &buf); - - BSON_ASSERT(bson_init_static(val->CODEWSCOPE.scope, buf, len)); - break; - } - case BCON_TYPE_INT32: - CHECK_TYPE(BSON_TYPE_INT32); - *val->INT32 = bson_iter_int32(iter); - break; - case BCON_TYPE_TIMESTAMP: - CHECK_TYPE(BSON_TYPE_TIMESTAMP); - bson_iter_timestamp(iter, val->TIMESTAMP.timestamp, val->TIMESTAMP.increment); - break; - case BCON_TYPE_INT64: - CHECK_TYPE(BSON_TYPE_INT64); - *val->INT64 = bson_iter_int64(iter); - break; - case BCON_TYPE_DECIMAL128: - CHECK_TYPE(BSON_TYPE_DECIMAL128); - BSON_ASSERT(bson_iter_decimal128(iter, val->DECIMAL128)); - break; - case BCON_TYPE_MAXKEY: - CHECK_TYPE(BSON_TYPE_MAXKEY); - break; - case BCON_TYPE_MINKEY: - CHECK_TYPE(BSON_TYPE_MINKEY); - break; - case BCON_TYPE_ARRAY: { - const uint8_t *buf; - uint32_t len; - - CHECK_TYPE(BSON_TYPE_ARRAY); - - bson_iter_array(iter, &len, &buf); - - BSON_ASSERT(bson_init_static(val->ARRAY, buf, len)); - break; - } - case BCON_TYPE_DOCUMENT: { - const uint8_t *buf; - uint32_t len; - - CHECK_TYPE(BSON_TYPE_DOCUMENT); - - bson_iter_document(iter, &len, &buf); - - BSON_ASSERT(bson_init_static(val->DOCUMENT, buf, len)); - break; - } - case BCON_TYPE_SKIP: - CHECK_TYPE(val->TYPE); - break; - case BCON_TYPE_ITER: - memcpy(val->ITER, iter, sizeof *iter); - break; - default: - BSON_ASSERT(0); - break; - } - - return true; -} - -/* Consumes ap, storing output values into u and returning the type of the - * captured token. - * - * The basic workflow goes like this: - * - * 1. Look at the current arg. It will be a char * - * a. If it's a NULL, we're done processing. - * b. If it's BCON_MAGIC (a symbol with storage in this module) - * I. The next token is the type - * II. The type specifies how many args to eat and their types - * c. Otherwise it's either recursion related or a raw string - * I. If the first byte is '{', '}', '[', or ']' pass back an - * appropriate recursion token - * II. If not, just call it a UTF8 token and pass that back - */ -static bcon_type_t -_bcon_append_tokenize(va_list *ap, bcon_append_t *u) -{ - char *mark; - bcon_type_t type; - - mark = va_arg(*ap, char *); - - BSON_ASSERT(mark != BCONE_MAGIC); - - if (mark == NULL) { - type = BCON_TYPE_END; - } else if (mark == BCON_MAGIC) { - type = va_arg(*ap, bcon_type_t); - - switch ((int)type) { - case BCON_TYPE_UTF8: - u->UTF8 = va_arg(*ap, char *); - break; - case BCON_TYPE_DOUBLE: - u->DOUBLE = va_arg(*ap, double); - break; - case BCON_TYPE_DOCUMENT: - u->DOCUMENT = va_arg(*ap, bson_t *); - break; - case BCON_TYPE_ARRAY: - u->ARRAY = va_arg(*ap, bson_t *); - break; - case BCON_TYPE_BIN: - u->BIN.subtype = va_arg(*ap, bson_subtype_t); - u->BIN.binary = va_arg(*ap, uint8_t *); - u->BIN.length = va_arg(*ap, uint32_t); - break; - case BCON_TYPE_UNDEFINED: - break; - case BCON_TYPE_OID: - u->OID = va_arg(*ap, bson_oid_t *); - break; - case BCON_TYPE_BOOL: - u->BOOL = va_arg(*ap, int); - break; - case BCON_TYPE_DATE_TIME: - u->DATE_TIME = va_arg(*ap, int64_t); - break; - case BCON_TYPE_NULL: - break; - case BCON_TYPE_REGEX: - u->REGEX.regex = va_arg(*ap, char *); - u->REGEX.flags = va_arg(*ap, char *); - break; - case BCON_TYPE_DBPOINTER: - u->DBPOINTER.collection = va_arg(*ap, char *); - u->DBPOINTER.oid = va_arg(*ap, bson_oid_t *); - break; - case BCON_TYPE_CODE: - u->CODE = va_arg(*ap, char *); - break; - case BCON_TYPE_SYMBOL: - u->SYMBOL = va_arg(*ap, char *); - break; - case BCON_TYPE_CODEWSCOPE: - u->CODEWSCOPE.js = va_arg(*ap, char *); - u->CODEWSCOPE.scope = va_arg(*ap, bson_t *); - break; - case BCON_TYPE_INT32: - u->INT32 = va_arg(*ap, int32_t); - break; - case BCON_TYPE_TIMESTAMP: - u->TIMESTAMP.timestamp = va_arg(*ap, uint32_t); - u->TIMESTAMP.increment = va_arg(*ap, uint32_t); - break; - case BCON_TYPE_INT64: - u->INT64 = va_arg(*ap, int64_t); - break; - case BCON_TYPE_DECIMAL128: - u->DECIMAL128 = va_arg(*ap, bson_decimal128_t *); - break; - case BCON_TYPE_MAXKEY: - break; - case BCON_TYPE_MINKEY: - break; - case BCON_TYPE_BCON: - u->BCON = va_arg(*ap, bson_t *); - break; - case BCON_TYPE_ITER: - u->ITER = va_arg(*ap, const bson_iter_t *); - break; - default: - BSON_ASSERT(0); - break; - } - } else { - switch (mark[0]) { - case '{': - type = BCON_TYPE_DOC_START; - break; - case '}': - type = BCON_TYPE_DOC_END; - break; - case '[': - type = BCON_TYPE_ARRAY_START; - break; - case ']': - type = BCON_TYPE_ARRAY_END; - break; - - default: - type = BCON_TYPE_UTF8; - u->UTF8 = mark; - break; - } - } - - return type; -} - - -/* Consumes ap, storing output values into u and returning the type of the - * captured token. - * - * The basic workflow goes like this: - * - * 1. Look at the current arg. It will be a char * - * a. If it's a NULL, we're done processing. - * b. If it's BCONE_MAGIC (a symbol with storage in this module) - * I. The next token is the type - * II. The type specifies how many args to eat and their types - * c. Otherwise it's either recursion related or a raw string - * I. If the first byte is '{', '}', '[', or ']' pass back an - * appropriate recursion token - * II. If not, just call it a UTF8 token and pass that back - */ -static bcon_type_t -_bcon_extract_tokenize(va_list *ap, bcon_extract_t *u) -{ - char *mark; - bcon_type_t type; - - mark = va_arg(*ap, char *); - - BSON_ASSERT(mark != BCON_MAGIC); - - if (mark == NULL) { - type = BCON_TYPE_END; - } else if (mark == BCONE_MAGIC) { - type = va_arg(*ap, bcon_type_t); - - switch ((int)type) { - case BCON_TYPE_UTF8: - u->UTF8 = va_arg(*ap, const char **); - break; - case BCON_TYPE_DOUBLE: - u->DOUBLE = va_arg(*ap, double *); - break; - case BCON_TYPE_DOCUMENT: - u->DOCUMENT = va_arg(*ap, bson_t *); - break; - case BCON_TYPE_ARRAY: - u->ARRAY = va_arg(*ap, bson_t *); - break; - case BCON_TYPE_BIN: - u->BIN.subtype = va_arg(*ap, bson_subtype_t *); - u->BIN.binary = va_arg(*ap, const uint8_t **); - u->BIN.length = va_arg(*ap, uint32_t *); - break; - case BCON_TYPE_UNDEFINED: - break; - case BCON_TYPE_OID: - u->OID = va_arg(*ap, const bson_oid_t **); - break; - case BCON_TYPE_BOOL: - u->BOOL = va_arg(*ap, bool *); - break; - case BCON_TYPE_DATE_TIME: - u->DATE_TIME = va_arg(*ap, int64_t *); - break; - case BCON_TYPE_NULL: - break; - case BCON_TYPE_REGEX: - u->REGEX.regex = va_arg(*ap, const char **); - u->REGEX.flags = va_arg(*ap, const char **); - break; - case BCON_TYPE_DBPOINTER: - u->DBPOINTER.collection = va_arg(*ap, const char **); - u->DBPOINTER.oid = va_arg(*ap, const bson_oid_t **); - break; - case BCON_TYPE_CODE: - u->CODE = va_arg(*ap, const char **); - break; - case BCON_TYPE_SYMBOL: - u->SYMBOL = va_arg(*ap, const char **); - break; - case BCON_TYPE_CODEWSCOPE: - u->CODEWSCOPE.js = va_arg(*ap, const char **); - u->CODEWSCOPE.scope = va_arg(*ap, bson_t *); - break; - case BCON_TYPE_INT32: - u->INT32 = va_arg(*ap, int32_t *); - break; - case BCON_TYPE_TIMESTAMP: - u->TIMESTAMP.timestamp = va_arg(*ap, uint32_t *); - u->TIMESTAMP.increment = va_arg(*ap, uint32_t *); - break; - case BCON_TYPE_INT64: - u->INT64 = va_arg(*ap, int64_t *); - break; - case BCON_TYPE_DECIMAL128: - u->DECIMAL128 = va_arg(*ap, bson_decimal128_t *); - break; - case BCON_TYPE_MAXKEY: - break; - case BCON_TYPE_MINKEY: - break; - case BCON_TYPE_SKIP: - u->TYPE = va_arg(*ap, bson_type_t); - break; - case BCON_TYPE_ITER: - u->ITER = va_arg(*ap, bson_iter_t *); - break; - default: - BSON_ASSERT(0); - break; - } - } else { - switch (mark[0]) { - case '{': - type = BCON_TYPE_DOC_START; - break; - case '}': - type = BCON_TYPE_DOC_END; - break; - case '[': - type = BCON_TYPE_ARRAY_START; - break; - case ']': - type = BCON_TYPE_ARRAY_END; - break; - - default: - type = BCON_TYPE_RAW; - u->key = mark; - break; - } - } - - return type; -} - - -/* This trivial utility function is useful for concatenating a bson object onto - * the end of another, ignoring the keys from the source bson object and - * continuing to use and increment the keys from the source. It's only useful - * when called from bcon_append_ctx_va */ -static void -_bson_concat_array(bson_t *dest, const bson_t *src, bcon_append_ctx_t *ctx) -{ - bson_iter_t iter; - const char *key; - char i_str[16]; - bool r; - - r = bson_iter_init(&iter, src); - - if (!r) { - fprintf(stderr, "Invalid BSON document, possible memory coruption.\n"); - return; - } - - STACK_I--; - - while (bson_iter_next(&iter)) { - bson_uint32_to_string(STACK_I, &key, i_str, sizeof i_str); - STACK_I++; - - BSON_ASSERT(bson_append_iter(dest, key, -1, &iter)); - } -} - - -/* Append_ctx_va consumes the va_list until NULL is found, appending into bson - * as tokens are found. It can receive or return an in-progress bson object - * via the ctx param. It can also operate on the middle of a va_list, and so - * can be wrapped inside of another varargs function. - * - * Note that passing in a va_list that isn't perferectly formatted for BCON - * ingestion will almost certainly result in undefined behavior - * - * The workflow relies on the passed ctx object, which holds a stack of bson - * objects, along with metadata (if the emedded layer is an array, and which - * element it is on if so). We iterate, generating tokens from the va_list, - * until we reach an END token. If any errors occur, we just blow up (the - * var_args stuff is already incredibly fragile to mistakes, and we have no way - * of introspecting, so just don't screw it up). - * - * There are also a few STACK_* macros in here which manipulate ctx that are - * defined up top. - * */ -void -bcon_append_ctx_va(bson_t *bson, bcon_append_ctx_t *ctx, va_list *ap) -{ - bcon_type_t type; - const char *key; - char i_str[16]; - - bcon_append_t u = {0}; - - while (1) { - if (STACK_IS_ARRAY) { - bson_uint32_to_string(STACK_I, &key, i_str, sizeof i_str); - STACK_I++; - } else { - type = _bcon_append_tokenize(ap, &u); - - if (type == BCON_TYPE_END) { - return; - } - - if (type == BCON_TYPE_DOC_END) { - STACK_POP_DOC(bson_append_document_end(STACK_BSON_PARENT, STACK_BSON_CHILD)); - continue; - } - - if (type == BCON_TYPE_BCON) { - bson_concat(STACK_BSON_CHILD, u.BCON); - continue; - } - - BSON_ASSERT(type == BCON_TYPE_UTF8); - - key = u.UTF8; - } - - type = _bcon_append_tokenize(ap, &u); - BSON_ASSERT(type != BCON_TYPE_END); - - switch ((int)type) { - case BCON_TYPE_BCON: - BSON_ASSERT(STACK_IS_ARRAY); - _bson_concat_array(STACK_BSON_CHILD, u.BCON, ctx); - - break; - case BCON_TYPE_DOC_START: - STACK_PUSH_DOC(bson_append_document_begin(STACK_BSON_PARENT, key, -1, STACK_BSON_CHILD)); - break; - case BCON_TYPE_DOC_END: - STACK_POP_DOC(bson_append_document_end(STACK_BSON_PARENT, STACK_BSON_CHILD)); - break; - case BCON_TYPE_ARRAY_START: - STACK_PUSH_ARRAY(bson_append_array_unsafe_begin(STACK_BSON_PARENT, key, -1, STACK_BSON_CHILD)); - break; - case BCON_TYPE_ARRAY_END: - STACK_POP_ARRAY(bson_append_array_end(STACK_BSON_PARENT, STACK_BSON_CHILD)); - break; - default: - _bcon_append_single(STACK_BSON_CHILD, type, key, &u); - - break; - } - } -} - - -/* extract_ctx_va consumes the va_list until NULL is found, extracting values - * as tokens are found. It can receive or return an in-progress bson object - * via the ctx param. It can also operate on the middle of a va_list, and so - * can be wrapped inside of another varargs function. - * - * Note that passing in a va_list that isn't perferectly formatted for BCON - * ingestion will almost certainly result in undefined behavior - * - * The workflow relies on the passed ctx object, which holds a stack of iterator - * objects, along with metadata (if the emedded layer is an array, and which - * element it is on if so). We iterate, generating tokens from the va_list, - * until we reach an END token. If any errors occur, we just blow up (the - * var_args stuff is already incredibly fragile to mistakes, and we have no way - * of introspecting, so just don't screw it up). - * - * There are also a few STACK_* macros in here which manipulate ctx that are - * defined up top. - * - * The function returns true if all tokens could be successfully matched, false - * otherwise. - * */ -bool -bcon_extract_ctx_va(bson_t *bson, bcon_extract_ctx_t *ctx, va_list *ap) -{ - bcon_type_t type; - const char *key; - bson_iter_t root_iter; - bson_iter_t current_iter; - char i_str[16]; - - bcon_extract_t u = {0}; - - BSON_ASSERT(bson_iter_init(&root_iter, bson)); - - while (1) { - if (STACK_IS_ARRAY) { - bson_uint32_to_string(STACK_I, &key, i_str, sizeof i_str); - STACK_I++; - } else { - type = _bcon_extract_tokenize(ap, &u); - - if (type == BCON_TYPE_END) { - return true; - } - - if (type == BCON_TYPE_DOC_END) { - STACK_POP_DOC(_noop()); - continue; - } - - BSON_ASSERT(type == BCON_TYPE_RAW); - - key = u.key; - } - - type = _bcon_extract_tokenize(ap, &u); - BSON_ASSERT(type != BCON_TYPE_END); - - if (type == BCON_TYPE_DOC_END) { - STACK_POP_DOC(_noop()); - } else if (type == BCON_TYPE_ARRAY_END) { - STACK_POP_ARRAY(_noop()); - } else { - memcpy(¤t_iter, STACK_ITER_CHILD, sizeof current_iter); - - if (!bson_iter_find(¤t_iter, key)) { - return false; - } - - switch ((int)type) { - case BCON_TYPE_DOC_START: - - if (bson_iter_type(¤t_iter) != BSON_TYPE_DOCUMENT) { - return false; - } - - STACK_PUSH_DOC(bson_iter_recurse(¤t_iter, STACK_ITER_CHILD)); - break; - case BCON_TYPE_ARRAY_START: - - if (bson_iter_type(¤t_iter) != BSON_TYPE_ARRAY) { - return false; - } - - STACK_PUSH_ARRAY(bson_iter_recurse(¤t_iter, STACK_ITER_CHILD)); - break; - default: - - if (!_bcon_extract_single(¤t_iter, type, &u)) { - return false; - } - - break; - } - } - } -} - -void -bcon_extract_ctx_init(bcon_extract_ctx_t *ctx) -{ - ctx->n = 0; - ctx->stack[0].is_array = false; -} - -bool -bcon_extract(bson_t *bson, ...) -{ - va_list ap; - bcon_extract_ctx_t ctx; - bool r; - - bcon_extract_ctx_init(&ctx); - - va_start(ap, bson); - - r = bcon_extract_ctx_va(bson, &ctx, &ap); - - va_end(ap); - - return r; -} - - -void -bcon_append(bson_t *bson, ...) -{ - va_list ap; - bcon_append_ctx_t ctx; - - bcon_append_ctx_init(&ctx); - - va_start(ap, bson); - - bcon_append_ctx_va(bson, &ctx, &ap); - - va_end(ap); -} - - -void -bcon_append_ctx(bson_t *bson, bcon_append_ctx_t *ctx, ...) -{ - va_list ap; - - va_start(ap, ctx); - - bcon_append_ctx_va(bson, ctx, &ap); - - va_end(ap); -} - - -void -bcon_extract_ctx(bson_t *bson, bcon_extract_ctx_t *ctx, ...) -{ - va_list ap; - - va_start(ap, ctx); - - bcon_extract_ctx_va(bson, ctx, &ap); - - va_end(ap); -} - -void -bcon_append_ctx_init(bcon_append_ctx_t *ctx) -{ - ctx->n = 0; - ctx->stack[0].is_array = 0; -} - - -bson_t * -bcon_new(void *unused, ...) -{ - va_list ap; - bcon_append_ctx_t ctx; - bson_t *bson; - - bcon_append_ctx_init(&ctx); - - bson = bson_new(); - - va_start(ap, unused); - - bcon_append_ctx_va(bson, &ctx, &ap); - - va_end(ap); - - return bson; -} diff --git a/bsonjs/common/common-md5.c b/bsonjs/common/common-md5.c deleted file mode 100644 index 05d59b1..0000000 --- a/bsonjs/common/common-md5.c +++ /dev/null @@ -1,394 +0,0 @@ -/* - Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgement in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - L. Peter Deutsch - ghost@aladdin.com - - */ -/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ -/* - Independent implementation of MD5 (RFC 1321). - - This code implements the MD5 Algorithm defined in RFC 1321, whose - text is available at - http://www.ietf.org/rfc/rfc1321.txt - The code is derived from the text of the RFC, including the test suite - (section A.5) but excluding the rest of Appendix A. It does not include - any code or documentation that is identified in the RFC as being - copyrighted. - - The original and principal author of md5.c is L. Peter Deutsch - . Other authors are noted in the change history - that follows (in reverse chronological order): - - 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order - either statically or dynamically; added missing #include - in library. - 2002-03-11 lpd Corrected argument list for main(), and added int return - type, in test program and T value program. - 2002-02-21 lpd Added missing #include in test program. - 2000-07-03 lpd Patched to eliminate warnings about "constant is - unsigned in ANSI C, signed in traditional"; made test program - self-checking. - 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. - 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). - 1999-05-03 lpd Original version. - */ - -/* - * The following MD5 implementation has been modified to use types as - * specified in libbson. - */ - -#include - -#include - -#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ -#if BSON_BYTE_ORDER == BSON_BIG_ENDIAN -#define BYTE_ORDER 1 -#else -#define BYTE_ORDER -1 -#endif - -#define T_MASK ((uint32_t)~0) -#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) -#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) -#define T3 0x242070db -#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) -#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) -#define T6 0x4787c62a -#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) -#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) -#define T9 0x698098d8 -#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) -#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) -#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) -#define T13 0x6b901122 -#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) -#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) -#define T16 0x49b40821 -#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) -#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) -#define T19 0x265e5a51 -#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) -#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) -#define T22 0x02441453 -#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) -#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) -#define T25 0x21e1cde6 -#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) -#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) -#define T28 0x455a14ed -#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) -#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) -#define T31 0x676f02d9 -#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) -#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) -#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) -#define T35 0x6d9d6122 -#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) -#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) -#define T38 0x4bdecfa9 -#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) -#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) -#define T41 0x289b7ec6 -#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) -#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) -#define T44 0x04881d05 -#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) -#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) -#define T47 0x1fa27cf8 -#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) -#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) -#define T50 0x432aff97 -#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) -#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) -#define T53 0x655b59c3 -#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) -#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) -#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) -#define T57 0x6fa87e4f -#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) -#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) -#define T60 0x4e0811a1 -#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) -#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) -#define T63 0x2ad7d2bb -#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) - - -static void -bson_md5_process(bson_md5_t *md5, const uint8_t *data) -{ - uint32_t a = md5->abcd[0]; - uint32_t b = md5->abcd[1]; - uint32_t c = md5->abcd[2]; - uint32_t d = md5->abcd[3]; - uint32_t t; - -#if BYTE_ORDER > 0 - /* Define storage only for big-endian CPUs. */ - uint32_t X[16]; -#else - /* Define storage for little-endian or both types of CPUs. */ - uint32_t xbuf[16]; - const uint32_t *X; -#endif - - { -#if BYTE_ORDER == 0 - /* - * Determine dynamically whether this is a big-endian or - * little-endian machine, since we can use a more efficient - * algorithm on the latter. - */ - static const int w = 1; - - if (*((const uint8_t *)&w)) /* dynamic little-endian */ -#endif -#if BYTE_ORDER <= 0 /* little-endian */ - { - /* - * On little-endian machines, we can process properly aligned - * data without copying it. - */ - if (!(((uintptr_t)data) & 3u)) { -/* data are properly aligned */ -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcast-align" -#endif - X = (const uint32_t *)data; -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } else { - /* not aligned */ - memcpy(xbuf, data, sizeof(xbuf)); - X = xbuf; - } - } -#endif -#if BYTE_ORDER == 0 - else /* dynamic big-endian */ -#endif -#if BYTE_ORDER >= 0 /* big-endian */ - { - /* - * On big-endian machines, we must arrange the bytes in the - * right order. - */ - const uint8_t *xp = data; - int i; - -#if BYTE_ORDER == 0 - X = xbuf; /* (dynamic only) */ -#else -#define xbuf X /* (static only) */ -#endif - for (i = 0; i < 16; ++i, xp += 4) - xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); - } -#endif - } - -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) - -/* Round 1. */ -/* Let [abcd k s i] denote the operation - a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ -#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) -#define SET(a, b, c, d, k, s, Ti) \ - t = a + F(b, c, d) + X[k] + Ti; \ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 0, 7, T1); - SET(d, a, b, c, 1, 12, T2); - SET(c, d, a, b, 2, 17, T3); - SET(b, c, d, a, 3, 22, T4); - SET(a, b, c, d, 4, 7, T5); - SET(d, a, b, c, 5, 12, T6); - SET(c, d, a, b, 6, 17, T7); - SET(b, c, d, a, 7, 22, T8); - SET(a, b, c, d, 8, 7, T9); - SET(d, a, b, c, 9, 12, T10); - SET(c, d, a, b, 10, 17, T11); - SET(b, c, d, a, 11, 22, T12); - SET(a, b, c, d, 12, 7, T13); - SET(d, a, b, c, 13, 12, T14); - SET(c, d, a, b, 14, 17, T15); - SET(b, c, d, a, 15, 22, T16); -#undef SET - -/* Round 2. */ -/* Let [abcd k s i] denote the operation - a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ -#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) -#define SET(a, b, c, d, k, s, Ti) \ - t = a + G(b, c, d) + X[k] + Ti; \ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 1, 5, T17); - SET(d, a, b, c, 6, 9, T18); - SET(c, d, a, b, 11, 14, T19); - SET(b, c, d, a, 0, 20, T20); - SET(a, b, c, d, 5, 5, T21); - SET(d, a, b, c, 10, 9, T22); - SET(c, d, a, b, 15, 14, T23); - SET(b, c, d, a, 4, 20, T24); - SET(a, b, c, d, 9, 5, T25); - SET(d, a, b, c, 14, 9, T26); - SET(c, d, a, b, 3, 14, T27); - SET(b, c, d, a, 8, 20, T28); - SET(a, b, c, d, 13, 5, T29); - SET(d, a, b, c, 2, 9, T30); - SET(c, d, a, b, 7, 14, T31); - SET(b, c, d, a, 12, 20, T32); -#undef SET - -/* Round 3. */ -/* Let [abcd k s t] denote the operation - a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define SET(a, b, c, d, k, s, Ti) \ - t = a + H(b, c, d) + X[k] + Ti; \ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 5, 4, T33); - SET(d, a, b, c, 8, 11, T34); - SET(c, d, a, b, 11, 16, T35); - SET(b, c, d, a, 14, 23, T36); - SET(a, b, c, d, 1, 4, T37); - SET(d, a, b, c, 4, 11, T38); - SET(c, d, a, b, 7, 16, T39); - SET(b, c, d, a, 10, 23, T40); - SET(a, b, c, d, 13, 4, T41); - SET(d, a, b, c, 0, 11, T42); - SET(c, d, a, b, 3, 16, T43); - SET(b, c, d, a, 6, 23, T44); - SET(a, b, c, d, 9, 4, T45); - SET(d, a, b, c, 12, 11, T46); - SET(c, d, a, b, 15, 16, T47); - SET(b, c, d, a, 2, 23, T48); -#undef SET - -/* Round 4. */ -/* Let [abcd k s t] denote the operation - a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ -#define I(x, y, z) ((y) ^ ((x) | ~(z))) -#define SET(a, b, c, d, k, s, Ti) \ - t = a + I(b, c, d) + X[k] + Ti; \ - a = ROTATE_LEFT(t, s) + b - /* Do the following 16 operations. */ - SET(a, b, c, d, 0, 6, T49); - SET(d, a, b, c, 7, 10, T50); - SET(c, d, a, b, 14, 15, T51); - SET(b, c, d, a, 5, 21, T52); - SET(a, b, c, d, 12, 6, T53); - SET(d, a, b, c, 3, 10, T54); - SET(c, d, a, b, 10, 15, T55); - SET(b, c, d, a, 1, 21, T56); - SET(a, b, c, d, 8, 6, T57); - SET(d, a, b, c, 15, 10, T58); - SET(c, d, a, b, 6, 15, T59); - SET(b, c, d, a, 13, 21, T60); - SET(a, b, c, d, 4, 6, T61); - SET(d, a, b, c, 11, 10, T62); - SET(c, d, a, b, 2, 15, T63); - SET(b, c, d, a, 9, 21, T64); -#undef SET - - /* Then perform the following additions. (That is increment each - of the four registers by the value it had before this block - was started.) */ - md5->abcd[0] += a; - md5->abcd[1] += b; - md5->abcd[2] += c; - md5->abcd[3] += d; -} - -void -mcommon_md5_init(bson_md5_t *pms) -{ - pms->count[0] = pms->count[1] = 0; - pms->abcd[0] = 0x67452301; - pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; - pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; - pms->abcd[3] = 0x10325476; -} - -void -mcommon_md5_append(bson_md5_t *pms, const uint8_t *data, uint32_t nbytes) -{ - const uint8_t *p = data; - uint32_t left = nbytes; - uint8_t offset = (pms->count[0] >> 3) & 63; - uint32_t nbits = (uint32_t)(nbytes << 3); - - if (nbytes <= 0) - return; - - /* Update the message length. */ - pms->count[1] += nbytes >> 29; - pms->count[0] += nbits; - if (pms->count[0] < nbits) - pms->count[1]++; - - /* Process an initial partial block. */ - if (offset) { - uint32_t copy = (offset + nbytes > 64u ? 64u - offset : nbytes); - - memcpy(pms->buf + offset, p, copy); - if (offset + copy < 64) - return; - p += copy; - left -= copy; - bson_md5_process(pms, pms->buf); - } - - /* Process full blocks. */ - for (; left >= 64; p += 64, left -= 64) - bson_md5_process(pms, p); - - /* Process a final partial block. */ - if (left) - memcpy(pms->buf, p, left); -} - - -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, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t data[8]; - int i; - - /* Save the length before padding. */ - for (i = 0; i < 8; ++i) - data[i] = (uint8_t)(pms->count[i >> 2] >> ((i & 3) << 3)); - /* Pad to 56 bytes mod 64. */ - mcommon_md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); - /* Append the length. */ - 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/bsonjs/common/common-thread.c b/bsonjs/common/common-thread.c deleted file mode 100644 index 9f3e07d..0000000 --- a/bsonjs/common/common-thread.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include - -#if defined(BSON_OS_UNIX) -int -mcommon_thread_create(bson_thread_t *thread, BSON_THREAD_FUN_TYPE(func), void *arg) -{ - BSON_ASSERT_PARAM(thread); - BSON_ASSERT_PARAM(func); - BSON_OPTIONAL_PARAM(arg); // optional. - return pthread_create(thread, NULL, func, arg); -} -int -mcommon_thread_join(bson_thread_t thread) -{ - return pthread_join(thread, NULL); -} - -#if defined(MONGOC_ENABLE_DEBUG_ASSERTIONS) && defined(BSON_OS_UNIX) -bool -mcommon_mutex_is_locked(bson_mutex_t *mutex) -{ - return mutex->valid_tid && pthread_equal(pthread_self(), mutex->lock_owner); -} -#endif - -#else -int -mcommon_thread_create(bson_thread_t *thread, BSON_THREAD_FUN_TYPE(func), void *arg) -{ - BSON_ASSERT_PARAM(thread); - BSON_ASSERT_PARAM(func); - BSON_OPTIONAL_PARAM(arg); // optional. - - *thread = (HANDLE)_beginthreadex(NULL, 0, func, arg, 0, NULL); - if (0 == *thread) { - return errno; - } - return 0; -} -int -mcommon_thread_join(bson_thread_t thread) -{ - int ret; - - /* zero indicates success for WaitForSingleObject. */ - ret = WaitForSingleObject(thread, INFINITE); - if (WAIT_OBJECT_0 != ret) { - return ret; - } - /* zero indicates failure for CloseHandle. */ - ret = CloseHandle(thread); - if (0 == ret) { - return 1; - } - return 0; -} -#endif From 8b1f702be47ae7949450f2036ed1152344e2e14c Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 4 Sep 2026 08:23:14 -0500 Subject: [PATCH 15/69] Revert setup.py build-time filtering, now handled by vendor.sh --- setup.py | 15 +-------------- vendor.sh | 9 +++++++++ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/setup.py b/setup.py index a246fa6..d7a1820 100644 --- a/setup.py +++ b/setup.py @@ -13,20 +13,10 @@ # limitations under the License. import glob -import os import sys from setuptools import setup, Extension -# Vendored libbson sources for features bsonjs doesn't use (BCON, MD5, -# the thread helpers) and that nothing else in the vendored tree calls -# into, confirmed via nm against the rest of the built extension. -_UNUSED_VENDORED_SOURCES = frozenset(( - "bson-bcon.c", - "common-md5.c", - "common-thread.c", -)) - libraries = [] define_macros = [("BSON_COMPILATION", 1), ("Py_LIMITED_API", "0x03090000")] @@ -48,10 +38,7 @@ ext_modules=[ Extension( "bsonjs", - sources=["bsonjs/bsonjs.c"] + [ - src for src in glob.glob("bsonjs/*/*.c") - if os.path.basename(src) not in _UNUSED_VENDORED_SOURCES - ], + sources=["bsonjs/bsonjs.c"] + glob.glob("bsonjs/*/*.c"), include_dirs=["bsonjs", "bsonjs/bson", "bsonjs/jsonsl", diff --git a/vendor.sh b/vendor.sh index c6cc053..962a32f 100644 --- a/vendor.sh +++ b/vendor.sh @@ -22,6 +22,15 @@ rsync -r mongo-c-driver/src/common/src/mlib/*.h mongo-c-driver/src/common/src/ml rsync -r mongo-c-driver/cmake-build/src/libbson/src/bson/*.[hc] bsonjs/bson/ +# Drop implementations for libbson features bsonjs doesn't use and that +# nothing else in the vendored tree calls into (BCON, MD5, the thread +# helpers). Their headers stay: bson.h includes bson-bcon.h, and +# common-thread-private.h is a real dependency of bson-context.c and +# common-b64.c. +rm -f bsonjs/bson/bson-bcon.c +rm -f bsonjs/common/common-md5.c +rm -f bsonjs/common/common-thread.c + # Ignore autogenerated config.h and common-config.h git diff -- bsonjs/bson/config.h | tee echo "**** Review libbson's autogenerated src/bson/config.h (above) for newly added (or removed) macros ****" From c4f17756d0b56f2c027b80088eed01805a67d7df Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Fri, 4 Sep 2026 13:54:44 -0500 Subject: [PATCH 16/69] Fix CodeQL c-cpp build after merging BSONJS_VERSION from main The manual-build-command's standalone gcc invocation didn't define BSONJS_VERSION, so it failed once the merge from main brought in bsonjs.c's __version__ macro. Read it from pyproject.toml the same way setup.py does. --- .github/workflows/codeql.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6805691..2230290 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -35,9 +35,10 @@ jobs: # means never compiling it here. manual-build-command: | PYINC=$(python3 -c "import sysconfig; print(sysconfig.get_paths()['include'])") + BSONJS_VERSION=$(python3 -c "import re; print(re.search(r'(?m)^version\s*=\s*\"([^\"]+)\"', open('pyproject.toml').read()).group(1))") gcc -c bsonjs/bsonjs.c -o /tmp/bsonjs.o \ -Ibsonjs -Ibsonjs/bson -Ibsonjs/jsonsl -Ibsonjs/common -I"$PYINC" \ - -DBSON_COMPILATION=1 -DPy_LIMITED_API=0x03090000 \ + -DBSON_COMPILATION=1 -DPy_LIMITED_API=0x03090000 "-DBSONJS_VERSION=\"$BSONJS_VERSION\"" \ -D_XOPEN_SOURCE=700 -D_BSD_SOURCE=1 -D_DEFAULT_SOURCE=1 - language: python build-mode: none From 776a964ece72a1a26657813be0c2477a1380df02 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 14:58:41 -0500 Subject: [PATCH 17/69] Add build-libbson.sh to build libbson 2.5.0 as a static library --- build-libbson.sh | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 build-libbson.sh diff --git a/build-libbson.sh b/build-libbson.sh new file mode 100755 index 0000000..40e3b37 --- /dev/null +++ b/build-libbson.sh @@ -0,0 +1,53 @@ +#!/bin/bash -ex + +set -o xtrace +set -o errexit + +# Version of libbson to build. +LIBBSON_VERSION=${LIBBSON_VERSION:-"2.5.0"} +if [ -z "$LIBBSON_VERSION" ] +then + echo "Did not provide a libbson revision ID to build" + exit 1 +fi + +# Setup working directory +WORKDIR="mongo-c-driver-${LIBBSON_VERSION}" +if [ ! -d "$WORKDIR" ] +then + git clone --depth 1 -b "$LIBBSON_VERSION" https://github.com/mongodb/mongo-c-driver.git "$WORKDIR" +fi + +DEFAULT_ARCH=$(uname -m) +MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET:-"10.15"} +CMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES:-${DEFAULT_ARCH}} +CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-"Release"} + +DEFAULT_INSTALL_DIR=$(pwd)/libbson +LIBBSON_INSTALL_DIR=${LIBBSON_INSTALL_DIR:-${DEFAULT_INSTALL_DIR}} +LIBBSON_INSTALL_DIR="$(cd "$(dirname "$LIBBSON_INSTALL_DIR")"; pwd)/$(basename "$LIBBSON_INSTALL_DIR")" + +echo "MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET}" +echo "CMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}" +echo "LIBBSON_INSTALL_DIR=${LIBBSON_INSTALL_DIR}" + +pushd "$WORKDIR" + git checkout "$LIBBSON_VERSION" + mkdir -p cmake-build + pushd cmake-build + cmake -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF \ + -DENABLE_MONGOC=OFF \ + -DENABLE_SHARED=OFF \ + -DENABLE_STATIC=ON \ + -DBUILD_SHARED_LIBS=OFF \ + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ + -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} \ + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} \ + -DCMAKE_INSTALL_PREFIX:PATH="$LIBBSON_INSTALL_DIR" \ + .. + cmake --build . --target clean + cmake --build . + cmake --build . --target install --config ${CMAKE_BUILD_TYPE} + popd +popd From 510c8088d3fa7f2241a12fa5320f7dbb90b2d115 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 14:58:55 -0500 Subject: [PATCH 18/69] Add CMakeLists.txt for scikit-build-core with static libbson --- CMakeLists.txt | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..979a02e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,79 @@ +cmake_minimum_required(VERSION 3.17) +project(bsonjs LANGUAGES C) + +find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) + +# Read the package version from pyproject.toml for BSONJS_VERSION. +file(READ "${CMAKE_CURRENT_SOURCE_DIR}/pyproject.toml" _pyproject) +string(REGEX MATCH "version = \"([^\"]*)\"" _ "${_pyproject}") +set(_package_version "${CMAKE_MATCH_1}") + +# --- libbson static library detection --- +set(LIBBSON_INSTALL_DIR "$ENV{LIBBSON_INSTALL_DIR}") +if(NOT LIBBSON_INSTALL_DIR) + set(LIBBSON_INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libbson") +endif() +get_filename_component(LIBBSON_INSTALL_DIR "${LIBBSON_INSTALL_DIR}" ABSOLUTE) + +# Auto-build libbson if the install dir does not contain a lib directory. +if(NOT EXISTS "${LIBBSON_INSTALL_DIR}/lib") + message(STATUS "libbson not found, building from source...") + execute_process( + COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/build-libbson.sh + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + RESULT_VARIABLE _build_result + ) + if(NOT _build_result EQUAL 0) + message(FATAL_ERROR "Failed to build libbson via build-libbson.sh") + endif() +endif() + +# Find headers: libbson 2.x installs to include/bson-X.Y.Z/bson/ +file(GLOB _bson_include_dirs "${LIBBSON_INSTALL_DIR}/include/bson-*/bson") +if(NOT _bson_include_dirs) + message(FATAL_ERROR "Could not find bson headers under ${LIBBSON_INSTALL_DIR}/include") +endif() +list(GET _bson_include_dirs 0 LIBBSON_INCLUDE_DIR) + +# Find static library +if(WIN32) + file(GLOB _bson_libs + "${LIBBSON_INSTALL_DIR}/lib/bson2-static.lib" + "${LIBBSON_INSTALL_DIR}/lib/bson2.lib" + "${LIBBSON_INSTALL_DIR}/lib/*/bson2-static.lib" + "${LIBBSON_INSTALL_DIR}/lib/*/bson2.lib" + ) +else() + file(GLOB _bson_libs + "${LIBBSON_INSTALL_DIR}/lib/libbson2.a" + "${LIBBSON_INSTALL_DIR}/lib*/libbson2.a" + ) +endif() +if(NOT _bson_libs) + message(FATAL_ERROR "Could not find libbson2.a static library under ${LIBBSON_INSTALL_DIR}/lib") +endif() +list(GET _bson_libs 0 LIBBSON_STATIC_LIB) + +# --- Build the extension module --- +Python_add_library(bsonjs MODULE WITH_SOABI "${CMAKE_CURRENT_SOURCE_DIR}/bsonjs/bsonjs.c") + +target_include_directories(bsonjs PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/bsonjs" + "${LIBBSON_INCLUDE_DIR}" +) + +target_compile_definitions(bsonjs PRIVATE + Py_LIMITED_API=0x03090000 + BSONJS_VERSION="${_package_version}" + BSON_STATIC +) + +if(WIN32) + target_link_libraries(bsonjs PRIVATE ${LIBBSON_STATIC_LIB} ws2_32) +elseif(APPLE) + target_link_libraries(bsonjs PRIVATE ${LIBBSON_STATIC_LIB}) +else() + target_link_libraries(bsonjs PRIVATE ${LIBBSON_STATIC_LIB} rt) +endif() + +install(TARGETS bsonjs DESTINATION .) From ae0a70a77e44eef899fc6cee939d22847a6cdd74 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 14:59:05 -0500 Subject: [PATCH 19/69] Migrate from setuptools to scikit-build-core --- MANIFEST.in | 11 -------- pyproject.toml | 15 +++++++---- setup.py | 70 -------------------------------------------------- 3 files changed, 10 insertions(+), 86 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 setup.py diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 3e663bb..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,11 +0,0 @@ -include README.rst -include CHANGELOG.rst -include LICENSE -recursive-include bsonjs LICENSE -recursive-include bsonjs *.h -recursive-include bsonjs *.th -recursive-include bsonjs *.py -exclude benchmark.py -exclude build-wheels.sh -exclude docker-build.sh -exclude vendor.sh diff --git a/pyproject.toml b/pyproject.toml index 5195552..3838d8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,6 @@ [build-system] -requires = ["setuptools>=65"] -build-backend = "setuptools.build_meta" - -[tool.setuptools] -packages = [] +requires = ["scikit-build-core>=0.10", "cmake>=3.17", "ninja"] +build-backend = "scikit_build_core.build" [project] name = "python-bsonjs" @@ -44,10 +41,18 @@ Homepage = "https://github.com/mongodb-labs/python-bsonjs" [project.optional-dependencies] test = ["pymongo>=4", "pytest"] +[tool.scikit-build] +wheel.packages = [] +wheel.py-api = "cp39" + [tool.cibuildwheel] test-command = "pytest {package}/test" test-extras = ["test"] skip = ["cp314t-*"] +before-build = "bash ./cibw_before_build.sh" + +[tool.cibuildwheel.environment] +LIBBSON_INSTALL_DIR = "./libbson" # Use abi3audit to catch issues with Limited API wheels [tool.cibuildwheel.linux] diff --git a/setup.py b/setup.py deleted file mode 100644 index 9740df7..0000000 --- a/setup.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright 2016 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. - -import glob -import re -import sys -from pathlib import Path - -from setuptools import setup, Extension - - -def _read_version(): - """Read the package version from pyproject.toml. - - Keeps bsonjs.__version__ from drifting out of sync with the - package version, since this extension has no pure-Python __init__.py - to derive it from package metadata at import time instead. - """ - text = (Path(__file__).parent / "pyproject.toml").read_text() - match = re.search(r'(?m)^version\s*=\s*"([^"]+)"', text) - if not match: - raise RuntimeError("Could not find version in pyproject.toml") - return match.group(1) - - -libraries = [] -define_macros = [("BSON_COMPILATION", 1), - ("Py_LIMITED_API", "0x03090000"), - ("BSONJS_VERSION", '"%s"' % _read_version())] -if sys.platform == "win32": - libraries.append("ws2_32") -else: - # libbson's mlib/time_point.h needs these feature test macros to see - # POSIX clock functions (clock_gettime, etc.). - define_macros += [("_XOPEN_SOURCE", "700"), - ("_BSD_SOURCE", 1), - ("_DEFAULT_SOURCE", 1)] - if sys.platform == "darwin": - define_macros.append(("_DARWIN_C_SOURCE", 1)) - else: - # librt may be needed for clock_gettime() - libraries.append("rt") - -setup( - ext_modules=[ - Extension( - "bsonjs", - sources=["bsonjs/bsonjs.c"] + glob.glob("bsonjs/*/*.c"), - include_dirs=["bsonjs", - "bsonjs/bson", - "bsonjs/jsonsl", - "bsonjs/common"], - py_limited_api=True, - define_macros=define_macros, - libraries=libraries - ) - ], - options={'bdist_wheel': {'py_limited_api': 'cp39'} } -) From 0cda5f42a377fd186e9d8dc5495e97f95484a0a2 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 14:59:12 -0500 Subject: [PATCH 20/69] Add cibw_before_build.sh to build libbson in cibuildwheel containers --- cibw_before_build.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100755 cibw_before_build.sh diff --git a/cibw_before_build.sh b/cibw_before_build.sh new file mode 100755 index 0000000..cc8558f --- /dev/null +++ b/cibw_before_build.sh @@ -0,0 +1,18 @@ +#!/bin/bash -ex + +# Install cmake if the runner does not provide a compatible version. +if ! command -v cmake >/dev/null 2>&1; then + pip install "cmake>=3.17,<4" +fi + +# Set macOS architecture from cibuildwheel. +if [[ "$CIBW_BUILD" == *"macosx_"* ]]; then + if [[ "$ARCHFLAGS" == *"arm64"* ]]; then + export CMAKE_OSX_ARCHITECTURES="arm64" + else + export CMAKE_OSX_ARCHITECTURES="x86_64" + fi + export MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET:-"10.15"} +fi + +CMAKE_BUILD_TYPE=Release bash ./build-libbson.sh From d5d3c98e45c58f594e6e3fc556374f4878437a1f Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 14:59:19 -0500 Subject: [PATCH 21/69] Remove vendored libbson sources and obsolete build scripts --- bsonjs/bson/bson-bcon.h | 249 -- bsonjs/bson/bson-clock.c | 118 - bsonjs/bson/bson-clock.h | 41 - bsonjs/bson/bson-context-private.h | 73 - bsonjs/bson/bson-context.c | 344 --- bsonjs/bson/bson-context.h | 64 - bsonjs/bson/bson-decimal128.c | 771 ------ bsonjs/bson/bson-decimal128.h | 62 - bsonjs/bson/bson-endian.h | 246 -- bsonjs/bson/bson-error-private.h | 39 - bsonjs/bson/bson-iso8601-private.h | 38 - bsonjs/bson/bson-iso8601.c | 282 --- bsonjs/bson/bson-iter.c | 2625 --------------------- bsonjs/bson/bson-iter.h | 554 ----- bsonjs/bson/bson-json-private.h | 31 - bsonjs/bson/bson-json.c | 2398 ------------------- bsonjs/bson/bson-json.h | 91 - bsonjs/bson/bson-keys.c | 152 -- bsonjs/bson/bson-keys.h | 38 - bsonjs/bson/bson-oid.c | 269 --- bsonjs/bson/bson-oid.h | 244 -- bsonjs/bson/bson-prelude.h | 19 - bsonjs/bson/bson-reader.c | 817 ------- bsonjs/bson/bson-reader.h | 114 - bsonjs/bson/bson-string.c | 555 ----- bsonjs/bson/bson-string.h | 72 - bsonjs/bson/bson-timegm-private.h | 51 - bsonjs/bson/bson-timegm.c | 668 ------ bsonjs/bson/bson-types.h | 475 ---- bsonjs/bson/bson-utf8.c | 364 --- bsonjs/bson/bson-utf8.h | 46 - bsonjs/bson/bson-value.c | 202 -- bsonjs/bson/bson-value.h | 40 - bsonjs/bson/bson-vector-private.h | 64 - bsonjs/bson/bson-vector.c | 686 ------ bsonjs/bson/bson-vector.h | 615 ----- bsonjs/bson/bson-version-functions.c | 75 - bsonjs/bson/bson-version-functions.h | 41 - bsonjs/bson/bson-writer.c | 271 --- bsonjs/bson/bson-writer.h | 57 - bsonjs/bson/bson.c | 2869 ----------------------- bsonjs/bson/bson.h | 1274 ---------- bsonjs/bson/bson_t-private.h | 106 - bsonjs/bson/bson_t.h | 61 - bsonjs/bson/compat.h | 202 -- bsonjs/bson/config.h | 159 -- bsonjs/bson/error.c | 186 -- bsonjs/bson/error.h | 94 - bsonjs/bson/macros.h | 417 ---- bsonjs/bson/memory.c | 512 ---- bsonjs/bson/memory.h | 71 - bsonjs/bson/validate-private.h | 37 - bsonjs/bson/validate.c | 568 ----- bsonjs/bson/version.h | 97 - bsonjs/common/common-atomic-private.h | 657 ------ bsonjs/common/common-atomic.c | 251 -- bsonjs/common/common-b64-private.h | 58 - bsonjs/common/common-b64.c | 539 ----- bsonjs/common/common-bits-private.h | 50 - bsonjs/common/common-bson-dsl-private.h | 1298 ---------- bsonjs/common/common-config.h | 10 - bsonjs/common/common-json-private.h | 397 ---- bsonjs/common/common-json.c | 765 ------ bsonjs/common/common-macros-private.h | 111 - bsonjs/common/common-md5-private.h | 45 - bsonjs/common/common-oid-private.h | 36 - bsonjs/common/common-oid.c | 33 - bsonjs/common/common-prelude.h | 28 - bsonjs/common/common-string-private.h | 673 ------ bsonjs/common/common-string.c | 372 --- bsonjs/common/common-thread-private.h | 213 -- bsonjs/common/common-utf8-private.h | 182 -- bsonjs/common/mlib/ckdint.h | 679 ------ bsonjs/common/mlib/cmp.h | 132 -- bsonjs/common/mlib/config.h | 419 ---- bsonjs/common/mlib/duration.h | 399 ---- bsonjs/common/mlib/intencode.h | 350 --- bsonjs/common/mlib/intutil.h | 145 -- bsonjs/common/mlib/loop.h | 165 -- bsonjs/common/mlib/platform.h | 53 - bsonjs/common/mlib/str.h | 1239 ---------- bsonjs/common/mlib/str_vec.h | 31 - bsonjs/common/mlib/test.h | 334 --- bsonjs/common/mlib/time_point.h | 346 --- bsonjs/common/mlib/timer.h | 173 -- bsonjs/common/mlib/vec.th | 470 ---- bsonjs/jsonsl/LICENSE | 20 - bsonjs/jsonsl/jsonsl.c | 1577 ------------- bsonjs/jsonsl/jsonsl.h | 976 -------- build-wheels.sh | 52 - docker-build.sh | 17 - vendor.sh | 41 - 92 files changed, 33950 deletions(-) delete mode 100644 bsonjs/bson/bson-bcon.h delete mode 100644 bsonjs/bson/bson-clock.c delete mode 100644 bsonjs/bson/bson-clock.h delete mode 100644 bsonjs/bson/bson-context-private.h delete mode 100644 bsonjs/bson/bson-context.c delete mode 100644 bsonjs/bson/bson-context.h delete mode 100644 bsonjs/bson/bson-decimal128.c delete mode 100644 bsonjs/bson/bson-decimal128.h delete mode 100644 bsonjs/bson/bson-endian.h delete mode 100644 bsonjs/bson/bson-error-private.h delete mode 100644 bsonjs/bson/bson-iso8601-private.h delete mode 100644 bsonjs/bson/bson-iso8601.c delete mode 100644 bsonjs/bson/bson-iter.c delete mode 100644 bsonjs/bson/bson-iter.h delete mode 100644 bsonjs/bson/bson-json-private.h delete mode 100644 bsonjs/bson/bson-json.c delete mode 100644 bsonjs/bson/bson-json.h delete mode 100644 bsonjs/bson/bson-keys.c delete mode 100644 bsonjs/bson/bson-keys.h delete mode 100644 bsonjs/bson/bson-oid.c delete mode 100644 bsonjs/bson/bson-oid.h delete mode 100644 bsonjs/bson/bson-prelude.h delete mode 100644 bsonjs/bson/bson-reader.c delete mode 100644 bsonjs/bson/bson-reader.h delete mode 100644 bsonjs/bson/bson-string.c delete mode 100644 bsonjs/bson/bson-string.h delete mode 100644 bsonjs/bson/bson-timegm-private.h delete mode 100644 bsonjs/bson/bson-timegm.c delete mode 100644 bsonjs/bson/bson-types.h delete mode 100644 bsonjs/bson/bson-utf8.c delete mode 100644 bsonjs/bson/bson-utf8.h delete mode 100644 bsonjs/bson/bson-value.c delete mode 100644 bsonjs/bson/bson-value.h delete mode 100644 bsonjs/bson/bson-vector-private.h delete mode 100644 bsonjs/bson/bson-vector.c delete mode 100644 bsonjs/bson/bson-vector.h delete mode 100644 bsonjs/bson/bson-version-functions.c delete mode 100644 bsonjs/bson/bson-version-functions.h delete mode 100644 bsonjs/bson/bson-writer.c delete mode 100644 bsonjs/bson/bson-writer.h delete mode 100644 bsonjs/bson/bson.c delete mode 100644 bsonjs/bson/bson.h delete mode 100644 bsonjs/bson/bson_t-private.h delete mode 100644 bsonjs/bson/bson_t.h delete mode 100644 bsonjs/bson/compat.h delete mode 100644 bsonjs/bson/config.h delete mode 100644 bsonjs/bson/error.c delete mode 100644 bsonjs/bson/error.h delete mode 100644 bsonjs/bson/macros.h delete mode 100644 bsonjs/bson/memory.c delete mode 100644 bsonjs/bson/memory.h delete mode 100644 bsonjs/bson/validate-private.h delete mode 100644 bsonjs/bson/validate.c delete mode 100644 bsonjs/bson/version.h delete mode 100644 bsonjs/common/common-atomic-private.h delete mode 100644 bsonjs/common/common-atomic.c delete mode 100644 bsonjs/common/common-b64-private.h delete mode 100644 bsonjs/common/common-b64.c delete mode 100644 bsonjs/common/common-bits-private.h delete mode 100644 bsonjs/common/common-bson-dsl-private.h delete mode 100644 bsonjs/common/common-config.h delete mode 100644 bsonjs/common/common-json-private.h delete mode 100644 bsonjs/common/common-json.c delete mode 100644 bsonjs/common/common-macros-private.h delete mode 100644 bsonjs/common/common-md5-private.h delete mode 100644 bsonjs/common/common-oid-private.h delete mode 100644 bsonjs/common/common-oid.c delete mode 100644 bsonjs/common/common-prelude.h delete mode 100644 bsonjs/common/common-string-private.h delete mode 100644 bsonjs/common/common-string.c delete mode 100644 bsonjs/common/common-thread-private.h delete mode 100644 bsonjs/common/common-utf8-private.h delete mode 100644 bsonjs/common/mlib/ckdint.h delete mode 100644 bsonjs/common/mlib/cmp.h delete mode 100644 bsonjs/common/mlib/config.h delete mode 100644 bsonjs/common/mlib/duration.h delete mode 100644 bsonjs/common/mlib/intencode.h delete mode 100644 bsonjs/common/mlib/intutil.h delete mode 100644 bsonjs/common/mlib/loop.h delete mode 100644 bsonjs/common/mlib/platform.h delete mode 100644 bsonjs/common/mlib/str.h delete mode 100644 bsonjs/common/mlib/str_vec.h delete mode 100644 bsonjs/common/mlib/test.h delete mode 100644 bsonjs/common/mlib/time_point.h delete mode 100644 bsonjs/common/mlib/timer.h delete mode 100644 bsonjs/common/mlib/vec.th delete mode 100644 bsonjs/jsonsl/LICENSE delete mode 100644 bsonjs/jsonsl/jsonsl.c delete mode 100644 bsonjs/jsonsl/jsonsl.h delete mode 100755 build-wheels.sh delete mode 100755 docker-build.sh delete mode 100644 vendor.sh diff --git a/bsonjs/bson/bson-bcon.h b/bsonjs/bson/bson-bcon.h deleted file mode 100644 index 7adcc40..0000000 --- a/bsonjs/bson/bson-bcon.h +++ /dev/null @@ -1,249 +0,0 @@ -/* - * @file bcon.h - * @brief BCON (BSON C Object Notation) Declarations - */ - -#include - -/* Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BCON_H_ -#define BCON_H_ - -// Include specific headers first, because bson.h tries to include this header itself: -#include -#include -// For other APIs, not properly grouped, but needed: -#include - - -BSON_BEGIN_DECLS - - -#define BCON_STACK_MAX 100 - -#define BCON_ENSURE_DECLARE(fun, type) \ - static BSON_INLINE type bcon_ensure_##fun(type _t) \ - { \ - return _t; \ - } - -#define BCON_ENSURE(fun, val) bcon_ensure_##fun(val) - -#define BCON_ENSURE_STORAGE(fun, val) bcon_ensure_##fun(&(val)) - -BCON_ENSURE_DECLARE(const_char_ptr, const char *) -BCON_ENSURE_DECLARE(const_char_ptr_ptr, const char **) -BCON_ENSURE_DECLARE(double, double) -BCON_ENSURE_DECLARE(double_ptr, double *) -BCON_ENSURE_DECLARE(const_bson_ptr, const bson_t *) -BCON_ENSURE_DECLARE(bson_ptr, bson_t *) -BCON_ENSURE_DECLARE(subtype, bson_subtype_t) -BCON_ENSURE_DECLARE(subtype_ptr, bson_subtype_t *) -BCON_ENSURE_DECLARE(const_uint8_ptr, const uint8_t *) -BCON_ENSURE_DECLARE(const_uint8_ptr_ptr, const uint8_t **) -BCON_ENSURE_DECLARE(uint32, uint32_t) -BCON_ENSURE_DECLARE(uint32_ptr, uint32_t *) -BCON_ENSURE_DECLARE(const_oid_ptr, const bson_oid_t *) -BCON_ENSURE_DECLARE(const_oid_ptr_ptr, const bson_oid_t **) -BCON_ENSURE_DECLARE(int32, int32_t) -BCON_ENSURE_DECLARE(int32_ptr, int32_t *) -BCON_ENSURE_DECLARE(int64, int64_t) -BCON_ENSURE_DECLARE(int64_ptr, int64_t *) -BCON_ENSURE_DECLARE(const_decimal128_ptr, const bson_decimal128_t *) -BCON_ENSURE_DECLARE(bool, bool) -BCON_ENSURE_DECLARE(bool_ptr, bool *) -BCON_ENSURE_DECLARE(bson_type, bson_type_t) -BCON_ENSURE_DECLARE(bson_iter_ptr, bson_iter_t *) -BCON_ENSURE_DECLARE(const_bson_iter_ptr, const bson_iter_t *) - -#define BCON_UTF8(_val) BCON_MAGIC, BCON_TYPE_UTF8, BCON_ENSURE(const_char_ptr, (_val)) -#define BCON_DOUBLE(_val) BCON_MAGIC, BCON_TYPE_DOUBLE, BCON_ENSURE(double, (_val)) -#define BCON_DOCUMENT(_val) BCON_MAGIC, BCON_TYPE_DOCUMENT, BCON_ENSURE(const_bson_ptr, (_val)) -#define BCON_ARRAY(_val) BCON_MAGIC, BCON_TYPE_ARRAY, BCON_ENSURE(const_bson_ptr, (_val)) -#define BCON_BIN(_subtype, _binary, _length) \ - BCON_MAGIC, BCON_TYPE_BIN, BCON_ENSURE(subtype, (_subtype)), BCON_ENSURE(const_uint8_ptr, (_binary)), \ - BCON_ENSURE(uint32, (_length)) -#define BCON_UNDEFINED BCON_MAGIC, BCON_TYPE_UNDEFINED -#define BCON_OID(_val) BCON_MAGIC, BCON_TYPE_OID, BCON_ENSURE(const_oid_ptr, (_val)) -#define BCON_BOOL(_val) BCON_MAGIC, BCON_TYPE_BOOL, BCON_ENSURE(bool, (_val)) -#define BCON_DATE_TIME(_val) BCON_MAGIC, BCON_TYPE_DATE_TIME, BCON_ENSURE(int64, (_val)) -#define BCON_NULL BCON_MAGIC, BCON_TYPE_NULL -#define BCON_REGEX(_regex, _flags) \ - BCON_MAGIC, BCON_TYPE_REGEX, BCON_ENSURE(const_char_ptr, (_regex)), BCON_ENSURE(const_char_ptr, (_flags)) -#define BCON_DBPOINTER(_collection, _oid) \ - BCON_MAGIC, BCON_TYPE_DBPOINTER, BCON_ENSURE(const_char_ptr, (_collection)), BCON_ENSURE(const_oid_ptr, (_oid)) -#define BCON_CODE(_val) BCON_MAGIC, BCON_TYPE_CODE, BCON_ENSURE(const_char_ptr, (_val)) -#define BCON_SYMBOL(_val) BCON_MAGIC, BCON_TYPE_SYMBOL, BCON_ENSURE(const_char_ptr, (_val)) -#define BCON_CODEWSCOPE(_js, _scope) \ - BCON_MAGIC, BCON_TYPE_CODEWSCOPE, BCON_ENSURE(const_char_ptr, (_js)), BCON_ENSURE(const_bson_ptr, (_scope)) -#define BCON_INT32(_val) BCON_MAGIC, BCON_TYPE_INT32, BCON_ENSURE(int32, (_val)) -#define BCON_TIMESTAMP(_timestamp, _increment) \ - BCON_MAGIC, BCON_TYPE_TIMESTAMP, BCON_ENSURE(int32, (_timestamp)), BCON_ENSURE(int32, (_increment)) -#define BCON_INT64(_val) BCON_MAGIC, BCON_TYPE_INT64, BCON_ENSURE(int64, (_val)) -#define BCON_DECIMAL128(_val) BCON_MAGIC, BCON_TYPE_DECIMAL128, BCON_ENSURE(const_decimal128_ptr, (_val)) -#define BCON_MAXKEY BCON_MAGIC, BCON_TYPE_MAXKEY -#define BCON_MINKEY BCON_MAGIC, BCON_TYPE_MINKEY -#define BCON(_val) BCON_MAGIC, BCON_TYPE_BCON, BCON_ENSURE(const_bson_ptr, (_val)) -#define BCON_ITER(_val) BCON_MAGIC, BCON_TYPE_ITER, BCON_ENSURE(const_bson_iter_ptr, (_val)) - -#define BCONE_UTF8(_val) BCONE_MAGIC, BCON_TYPE_UTF8, BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_val)) -#define BCONE_DOUBLE(_val) BCONE_MAGIC, BCON_TYPE_DOUBLE, BCON_ENSURE_STORAGE(double_ptr, (_val)) -#define BCONE_DOCUMENT(_val) BCONE_MAGIC, BCON_TYPE_DOCUMENT, BCON_ENSURE_STORAGE(bson_ptr, (_val)) -#define BCONE_ARRAY(_val) BCONE_MAGIC, BCON_TYPE_ARRAY, BCON_ENSURE_STORAGE(bson_ptr, (_val)) -#define BCONE_BIN(subtype, binary, length) \ - BCONE_MAGIC, BCON_TYPE_BIN, BCON_ENSURE_STORAGE(subtype_ptr, (subtype)), \ - BCON_ENSURE_STORAGE(const_uint8_ptr_ptr, (binary)), BCON_ENSURE_STORAGE(uint32_ptr, (length)) -#define BCONE_UNDEFINED BCONE_MAGIC, BCON_TYPE_UNDEFINED -#define BCONE_OID(_val) BCONE_MAGIC, BCON_TYPE_OID, BCON_ENSURE_STORAGE(const_oid_ptr_ptr, (_val)) -#define BCONE_BOOL(_val) BCONE_MAGIC, BCON_TYPE_BOOL, BCON_ENSURE_STORAGE(bool_ptr, (_val)) -#define BCONE_DATE_TIME(_val) BCONE_MAGIC, BCON_TYPE_DATE_TIME, BCON_ENSURE_STORAGE(int64_ptr, (_val)) -#define BCONE_NULL BCONE_MAGIC, BCON_TYPE_NULL -#define BCONE_REGEX(_regex, _flags) \ - BCONE_MAGIC, BCON_TYPE_REGEX, BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_regex)), \ - BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_flags)) -#define BCONE_DBPOINTER(_collection, _oid) \ - BCONE_MAGIC, BCON_TYPE_DBPOINTER, BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_collection)), \ - BCON_ENSURE_STORAGE(const_oid_ptr_ptr, (_oid)) -#define BCONE_CODE(_val) BCONE_MAGIC, BCON_TYPE_CODE, BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_val)) -#define BCONE_SYMBOL(_val) BCONE_MAGIC, BCON_TYPE_SYMBOL, BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_val)) -#define BCONE_CODEWSCOPE(_js, _scope) \ - BCONE_MAGIC, BCON_TYPE_CODEWSCOPE, BCON_ENSURE_STORAGE(const_char_ptr_ptr, (_js)), \ - BCON_ENSURE_STORAGE(bson_ptr, (_scope)) -#define BCONE_INT32(_val) BCONE_MAGIC, BCON_TYPE_INT32, BCON_ENSURE_STORAGE(int32_ptr, (_val)) -#define BCONE_TIMESTAMP(_timestamp, _increment) \ - BCONE_MAGIC, BCON_TYPE_TIMESTAMP, BCON_ENSURE_STORAGE(int32_ptr, (_timestamp)), \ - BCON_ENSURE_STORAGE(int32_ptr, (_increment)) -#define BCONE_INT64(_val) BCONE_MAGIC, BCON_TYPE_INT64, BCON_ENSURE_STORAGE(int64_ptr, (_val)) -#define BCONE_DECIMAL128(_val) BCONE_MAGIC, BCON_TYPE_DECIMAL128, BCON_ENSURE_STORAGE(const_decimal128_ptr, (_val)) -#define BCONE_MAXKEY BCONE_MAGIC, BCON_TYPE_MAXKEY -#define BCONE_MINKEY BCONE_MAGIC, BCON_TYPE_MINKEY -#define BCONE_SKIP(_val) BCONE_MAGIC, BCON_TYPE_SKIP, BCON_ENSURE(bson_type, (_val)) -#define BCONE_ITER(_val) BCONE_MAGIC, BCON_TYPE_ITER, BCON_ENSURE_STORAGE(bson_iter_ptr, (_val)) - -#define BCON_MAGIC bson_bcon_magic() -#define BCONE_MAGIC bson_bcone_magic() - -typedef enum { - BCON_TYPE_UTF8, - BCON_TYPE_DOUBLE, - BCON_TYPE_DOCUMENT, - BCON_TYPE_ARRAY, - BCON_TYPE_BIN, - BCON_TYPE_UNDEFINED, - BCON_TYPE_OID, - BCON_TYPE_BOOL, - BCON_TYPE_DATE_TIME, - BCON_TYPE_NULL, - BCON_TYPE_REGEX, - BCON_TYPE_DBPOINTER, - BCON_TYPE_CODE, - BCON_TYPE_SYMBOL, - BCON_TYPE_CODEWSCOPE, - BCON_TYPE_INT32, - BCON_TYPE_TIMESTAMP, - BCON_TYPE_INT64, - BCON_TYPE_DECIMAL128, - BCON_TYPE_MAXKEY, - BCON_TYPE_MINKEY, - BCON_TYPE_BCON, - BCON_TYPE_ARRAY_START, - BCON_TYPE_ARRAY_END, - BCON_TYPE_DOC_START, - BCON_TYPE_DOC_END, - BCON_TYPE_END, - BCON_TYPE_RAW, - BCON_TYPE_SKIP, - BCON_TYPE_ITER, - BCON_TYPE_ERROR, -} bcon_type_t; - -typedef struct bcon_append_ctx_frame { - int i; - bool is_array; - bson_t bson; -} bcon_append_ctx_frame_t; - -typedef struct bcon_extract_ctx_frame { - int i; - bool is_array; - bson_iter_t iter; -} bcon_extract_ctx_frame_t; - -typedef struct _bcon_append_ctx_t { - bcon_append_ctx_frame_t stack[BCON_STACK_MAX]; - int n; -} bcon_append_ctx_t; - -typedef struct _bcon_extract_ctx_t { - bcon_extract_ctx_frame_t stack[BCON_STACK_MAX]; - int n; -} bcon_extract_ctx_t; - -BSON_EXPORT(void) -bcon_append(bson_t *bson, ...) BSON_GNUC_NULL_TERMINATED; -BSON_EXPORT(void) -bcon_append_ctx(bson_t *bson, bcon_append_ctx_t *ctx, ...) BSON_GNUC_NULL_TERMINATED; -BSON_EXPORT(void) -bcon_append_ctx_va(bson_t *bson, bcon_append_ctx_t *ctx, va_list *va); -BSON_EXPORT(void) -bcon_append_ctx_init(bcon_append_ctx_t *ctx); - -BSON_EXPORT(void) -bcon_extract_ctx_init(bcon_extract_ctx_t *ctx); - -BSON_EXPORT(void) -bcon_extract_ctx(bson_t *bson, bcon_extract_ctx_t *ctx, ...) BSON_GNUC_NULL_TERMINATED; - -BSON_EXPORT(bool) -bcon_extract_ctx_va(bson_t *bson, bcon_extract_ctx_t *ctx, va_list *ap); - -BSON_EXPORT(bool) -bcon_extract(bson_t *bson, ...) BSON_GNUC_NULL_TERMINATED; - -BSON_EXPORT(bool) -bcon_extract_va(bson_t *bson, bcon_extract_ctx_t *ctx, ...) BSON_GNUC_NULL_TERMINATED; - -BSON_EXPORT(bson_t *) -bcon_new(void *unused, ...) BSON_GNUC_NULL_TERMINATED; - -/** - * The bcon_..() functions are all declared with __attribute__((sentinel)). - * - * From GCC manual for "sentinel": "A valid NULL in this context is defined as - * zero with any pointer type. If your system defines the NULL macro with an - * integer type then you need to add an explicit cast." - * Case in point: GCC on Solaris (at least) - */ -#define BCON_APPEND(_bson, ...) bcon_append((_bson), __VA_ARGS__, (void *)NULL) -#define BCON_APPEND_CTX(_bson, _ctx, ...) bcon_append_ctx((_bson), (_ctx), __VA_ARGS__, (void *)NULL) - -#define BCON_EXTRACT(_bson, ...) bcon_extract((_bson), __VA_ARGS__, (void *)NULL) - -#define BCON_EXTRACT_CTX(_bson, _ctx, ...) bcon_extract((_bson), (_ctx), __VA_ARGS__, (void *)NULL) - -#define BCON_NEW(...) bcon_new(NULL, __VA_ARGS__, (void *)NULL) - -BSON_EXPORT(const char *) -bson_bcon_magic(void) BSON_GNUC_PURE; -BSON_EXPORT(const char *) -bson_bcone_magic(void) BSON_GNUC_PURE; - - -BSON_END_DECLS - - -#endif diff --git a/bsonjs/bson/bson-clock.c b/bsonjs/bson/bson-clock.c deleted file mode 100644 index 83f66bb..0000000 --- a/bsonjs/bson/bson-clock.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include -#include - - -#if defined(BSON_HAVE_CLOCK_GETTIME) -#include - -#include -#endif - -#include - -/* - *-------------------------------------------------------------------------- - * - * bson_gettimeofday -- - * - * A wrapper around gettimeofday() with fallback support for Windows. - * - * Returns: - * 0 if successful. - * - * Side effects: - * @tv is set. - * - *-------------------------------------------------------------------------- - */ - -int -bson_gettimeofday(struct timeval *tv) /* OUT */ -{ -#if defined(_WIN32) -#if defined(_MSC_VER) -#define DELTA_EPOCH_IN_MICROSEC 11644473600000000Ui64 -#else -#define DELTA_EPOCH_IN_MICROSEC 11644473600000000ULL -#endif - FILETIME ft; - uint64_t tmp = 0; - - /* - * The const value is shamelessly stolen from - * http://www.boost.org/doc/libs/1_55_0/boost/chrono/detail/inlined/win/chrono.hpp - * - * File times are the number of 100 nanosecond intervals elapsed since - * 12:00 am Jan 1, 1601 UTC. I haven't check the math particularly hard - * - * ... good luck - */ - - if (tv) { - GetSystemTimeAsFileTime(&ft); - - /* pull out of the filetime into a 64 bit uint */ - tmp |= ft.dwHighDateTime; - tmp <<= 32; - tmp |= ft.dwLowDateTime; - - /* convert from 100's of nanosecs to microsecs */ - tmp /= 10; - - /* adjust to unix epoch */ - tmp -= DELTA_EPOCH_IN_MICROSEC; - - tv->tv_sec = (long)(tmp / 1000000UL); - tv->tv_usec = (long)(tmp % 1000000UL); - } - - return 0; -#else - return gettimeofday(tv, NULL); -#endif -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_get_monotonic_time -- - * - * Returns the monotonic system time, if available. A best effort is - * made to use the monotonic clock. However, some systems may not - * support such a feature. - * - * Returns: - * The monotonic clock in microseconds. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -int64_t -bson_get_monotonic_time(void) -{ - mlib_time_point now = mlib_now(); - return mlib_microseconds_count(now.time_since_monotonic_start); -} diff --git a/bsonjs/bson/bson-clock.h b/bsonjs/bson/bson-clock.h deleted file mode 100644 index 5f7d8df..0000000 --- a/bsonjs/bson/bson-clock.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_CLOCK_H -#define BSON_CLOCK_H - - -#include -#include -#include - - -BSON_BEGIN_DECLS - - -BSON_EXPORT(int64_t) -bson_get_monotonic_time(void); -BSON_EXPORT(int) -bson_gettimeofday(struct timeval *tv); - - -BSON_END_DECLS - - -#endif /* BSON_CLOCK_H */ diff --git a/bsonjs/bson/bson-context-private.h b/bsonjs/bson/bson-context-private.h deleted file mode 100644 index 8097df4..0000000 --- a/bsonjs/bson/bson-context-private.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_CONTEXT_PRIVATE_H -#define BSON_CONTEXT_PRIVATE_H - -#include // IWYU pragma: export - -// - -#include - - -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; - 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); - -BSON_END_DECLS - - -#endif /* BSON_CONTEXT_PRIVATE_H */ diff --git a/bsonjs/bson/bson-context.c b/bsonjs/bson/bson-context.c deleted file mode 100644 index 431468d..0000000 --- a/bsonjs/bson/bson-context.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - - -#ifndef HOST_NAME_MAX -#define HOST_NAME_MAX 256 -#endif - - -/* - * Globals. - */ -static bson_context_t gContextDefault; - -static BSON_INLINE uint64_t -_bson_getpid(void) -{ - uint64_t pid; -#ifdef BSON_OS_WIN32 - DWORD real_pid; - - real_pid = GetCurrentProcessId(); - pid = (real_pid & 0xFFFF) ^ ((real_pid >> 16) & 0xFFFF); -#else - pid = (uint64_t)getpid(); -#endif - - return pid; -} - - -void -_bson_context_set_oid_seq32(bson_context_t *context, /* IN */ - bson_oid_t *oid) /* OUT */ -{ - uint32_t seq = (uint32_t)mcommon_atomic_int32_fetch_add( - (DECL_ATOMIC_INTEGRAL_INT32 *)&context->seq32, 1, mcommon_memory_order_seq_cst); - seq = BSON_UINT32_TO_BE(seq); - memcpy(&oid->bytes[BSON_OID_SEQ32_OFFSET], ((uint8_t *)&seq) + 1, BSON_OID_SEQ32_SIZE); -} - -/* - * -------------------------------------------------------------------------- - * - * _bson_context_get_hostname - * - * Gets the hostname of the machine, logs a warning on failure. "out" - * must be an array of HOST_NAME_MAX bytes. - * - * -------------------------------------------------------------------------- - */ -static void -_bson_context_get_hostname(char out[HOST_NAME_MAX]) -{ - if (gethostname(out, HOST_NAME_MAX) != 0) { - if (errno == ENAMETOOLONG) { - fprintf(stderr, "hostname exceeds %d characters, truncating.", HOST_NAME_MAX); - } else { - fprintf(stderr, "unable to get hostname: %d", errno); - } - } - out[HOST_NAME_MAX - 1] = '\0'; -} - - -/*** ======================================== - * 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) -{ - *p = (*p << nbits) | (*p >> (64 - nbits)); -} - -/* 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 = mlib_read_u64le(kk); - uint64_t k1 = mlib_read_u64le(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 = mlib_read_u64le(ni); - v3 ^= m; - - for (i = 0; i < C_ROUNDS; ++i) - _sip_round(&v0, &v1, &v2, &v3); - - v0 ^= m; - } - - 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; - } - - 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; - mlib_write_u64le(digest_buf, b); - - v1 ^= 0xdd; - - for (i = 0; i < D_ROUNDS; ++i) - _sip_round(&v0, &v1, &v2, &v3); - - b = v0 ^ v1 ^ v2 ^ v3; - mlib_write_u64le(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 = - mcommon_atomic_int64_fetch_add(&s_rand_call_counter, 1, mcommon_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; - _bson_context_init_random(context, true /* Init counters */); -} - - -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_t * -bson_context_new(bson_context_flags_t flags) -{ - bson_context_t *context; - - context = bson_malloc0(sizeof *context); - _bson_context_init(context, flags); - - return context; -} - - -void -bson_context_destroy(bson_context_t *context) /* IN */ -{ - bson_free(context); -} - -static BSON_ONCE_FUN(_bson_context_init_default) -{ - _bson_context_init(&gContextDefault, BSON_CONTEXT_DISABLE_PID_CACHE); - BSON_ONCE_RETURN; -} - - -bson_context_t * -bson_context_get_default(void) -{ - static bson_once_t once = BSON_ONCE_INIT; - - bson_once(&once, _bson_context_init_default); - - return &gContextDefault; -} diff --git a/bsonjs/bson/bson-context.h b/bsonjs/bson/bson-context.h deleted file mode 100644 index 26d03cb..0000000 --- a/bsonjs/bson/bson-context.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_CONTEXT_H -#define BSON_CONTEXT_H - - -#include -#include - - -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); - - -BSON_END_DECLS - - -#endif /* BSON_CONTEXT_H */ diff --git a/bsonjs/bson/bson-decimal128.c b/bsonjs/bson/bson-decimal128.c deleted file mode 100644 index 786b565..0000000 --- a/bsonjs/bson/bson-decimal128.c +++ /dev/null @@ -1,771 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include - -#include -#include - -#include -#include - -#include -#include -#include - - -#define BSON_DECIMAL128_EXPONENT_MAX 6111 -#define BSON_DECIMAL128_EXPONENT_MIN -6176 -#define BSON_DECIMAL128_EXPONENT_BIAS 6176 -#define BSON_DECIMAL128_MAX_DIGITS 34 - -#define BSON_DECIMAL128_SET_NAN(dec) \ - if (1) { \ - (dec).high = 0x7c00000000000000ull; \ - (dec).low = 0; \ - } else \ - (void)0 -#define BSON_DECIMAL128_SET_INF(dec, isneg) \ - if (1) { \ - (dec).high = 0x7800000000000000ull + 0x8000000000000000ull * (isneg); \ - (dec).low = 0; \ - } else \ - (void)0 - -/** - * _bson_uint128_t: - * - * This struct represents a 128 bit integer. - */ -typedef struct { - uint32_t parts[4]; /* 32-bit words stored high to low. */ -} _bson_uint128_t; - - -/** - *------------------------------------------------------------------------------ - * - * _bson_uint128_divide1B -- - * - * This function divides a #_bson_uint128_t by 1000000000 (1 billion) and - * computes the quotient and remainder. - * - * The remainder will contain 9 decimal digits for conversion to string. - * - * @value The #_bson_uint128_t operand. - * @quotient A pointer to store the #_bson_uint128_t quotient. - * @rem A pointer to store the #uint64_t remainder. - * - * Returns: - * The quotient at @quotient and the remainder at @rem. - * - * Side effects: - * None. - * - *------------------------------------------------------------------------------ - */ -static void -_bson_uint128_divide1B(_bson_uint128_t value, /* IN */ - _bson_uint128_t *quotient, /* OUT */ - uint32_t *rem) /* OUT */ -{ - const uint32_t DIVISOR = 1000 * 1000 * 1000; - uint64_t _rem = 0; - int i = 0; - - if (!value.parts[0] && !value.parts[1] && !value.parts[2] && !value.parts[3]) { - *quotient = value; - *rem = 0; - return; - } - - - for (i = 0; i <= 3; i++) { - _rem <<= 32; /* Adjust remainder to match value of next dividend */ - _rem += value.parts[i]; /* Add the divided to _rem */ - value.parts[i] = (uint32_t)(_rem / DIVISOR); - _rem %= DIVISOR; /* Store the remainder */ - } - - *quotient = value; - *rem = (uint32_t)_rem; -} - - -/** - *------------------------------------------------------------------------------ - * - * bson_decimal128_to_string -- - * - * This function converts a BID formatted decimal128 value to string, - * accepting a &bson_decimal128_t as @dec. The string is stored at @str. - * - * @dec : The BID formatted decimal to convert. - * @str : The output decimal128 string. At least %BSON_DECIMAL128_STRING - *characters. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *------------------------------------------------------------------------------ - */ -void -bson_decimal128_to_string(const bson_decimal128_t *dec, /* IN */ - char *str) /* OUT */ -{ - const int COMBINATION_MASK = 0x1f; /* Extract least significant 5 bits */ - const int EXPONENT_MASK = 0x3fff; /* Extract least significant 14 bits */ - const int COMBINATION_INFINITY = 30; /* Value of combination field for Inf */ - const int COMBINATION_NAN = 31; /* Value of combination field for NaN */ - const int EXPONENT_BIAS = 6176; /* decimal128 exponent bias */ - - char *str_out = str; /* output pointer in string */ - char significand_str[35]; /* decoded significand digits */ - - /* Note: bits in this routine are referred to starting at 0, */ - /* from the sign bit, towards the coefficient. */ - uint32_t high; /* bits 0 - 31 */ - uint32_t midh; /* bits 32 - 63 */ - uint32_t midl; /* bits 64 - 95 */ - uint32_t low; /* bits 96 - 127 */ - uint32_t combination; /* bits 1 - 5 */ - uint32_t significand[36] = {0}; /* the base-10 digits in the significand */ - uint32_t *significand_read = significand; /* read pointer into significand */ - bool is_zero = false; /* true if the number is zero */ - - uint8_t significand_msb; /* the most signifcant significand bits (50-46) */ - _bson_uint128_t significand128; /* temporary storage for significand decoding */ - - memset(significand_str, 0, sizeof(significand_str)); - - if ((int64_t)dec->high < 0) { /* negative */ - *(str_out++) = '-'; - } - - low = (uint32_t)dec->low; - midl = (uint32_t)(dec->low >> 32); - midh = (uint32_t)dec->high; - high = (uint32_t)(dec->high >> 32); - - /* Decode combination field and exponent */ - combination = (high >> 26) & COMBINATION_MASK; - - int biased_exponent; - if (BSON_UNLIKELY((combination >> 3) == 3)) { - /* Check for 'special' values */ - if (mlib_cmp(combination, ==, COMBINATION_INFINITY)) { /* Infinity */ - strcpy(str_out, BSON_DECIMAL128_INF); - return; - } else if (mlib_cmp(combination, ==, COMBINATION_NAN)) { /* NaN */ - /* str, not str_out, to erase the sign */ - strcpy(str, BSON_DECIMAL128_NAN); - /* we don't care about the NaN payload. */ - return; - } else { - biased_exponent = (high >> 15) & EXPONENT_MASK; - significand_msb = 0x8 + ((high >> 14) & 0x1); - } - } else { - significand_msb = (high >> 14) & 0x7; - biased_exponent = (high >> 17) & EXPONENT_MASK; - } - - /* unbiased exponent */ - const int32_t exponent = biased_exponent - EXPONENT_BIAS; - - /* Create string of significand digits */ - - /* Convert the 114-bit binary number represented by */ - /* (high, midh, midl, low) to at most 34 decimal */ - /* digits through modulo and division. */ - significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xfu) << 14); - significand128.parts[1] = midh; - significand128.parts[2] = midl; - significand128.parts[3] = low; - - if (significand128.parts[0] == 0 && significand128.parts[1] == 0 && significand128.parts[2] == 0 && - significand128.parts[3] == 0) { - is_zero = true; - } else if (significand128.parts[0] >= (1 << 17)) { - /* The significand is non-canonical or zero. - * In order to preserve compatibility with the densely packed decimal - * format, the maximum value for the significand of decimal128 is - * 1e34 - 1. If the value is greater than 1e34 - 1, the IEEE 754 - * standard dictates that the significand is interpreted as zero. - */ - is_zero = true; - } else { - for (int k = 3; k >= 0; k--) { - uint32_t least_digits = 0; - _bson_uint128_divide1B(significand128, &significand128, &least_digits); - - /* We now have the 9 least significant digits (in base 2). */ - /* Convert and output to string. */ - if (!least_digits) { - continue; - } - - for (int j = 8; j >= 0; j--) { - significand[k * 9 + j] = least_digits % 10; - least_digits /= 10; - } - } - } - - /* Output format options: */ - /* Scientific - [-]d.dddE(+/-)dd or [-]dE(+/-)dd */ - /* Regular - ddd.ddd */ - - int32_t num_significant_digits; - if (is_zero) { - num_significant_digits = 1; - *significand_read = 0; - } else { - num_significant_digits = 36; - while (!(*significand_read)) { - num_significant_digits--; - significand_read++; - } - } - - /* the exponent if scientific notation is used */ - const int32_t scientific_exponent = num_significant_digits - 1 + exponent; - - /* The scientific exponent checks are dictated by the string conversion - * specification and are somewhat arbitrary cutoffs. - * - * We must check exponent > 0, because if this is the case, the number - * has trailing zeros. However, we *cannot* output these trailing zeros, - * because doing so would change the precision of the value, and would - * change stored data if the string converted number is round tripped. - */ - if (scientific_exponent < -6 || exponent > 0) { - /* Scientific format */ - *(str_out++) = *(significand_read++) + '0'; - num_significant_digits--; - - if (num_significant_digits) { - *(str_out++) = '.'; - } - - for (int32_t i = 0; i < num_significant_digits && (str_out - str) < 36; i++) { - *(str_out++) = *(significand_read++) + '0'; - } - /* Exponent */ - *(str_out++) = 'E'; - // Truncation is OK. - int req = bson_snprintf(str_out, 6, "%+d", scientific_exponent); - BSON_ASSERT(req > 0); - } else { - /* Regular format with no decimal place */ - if (exponent >= 0) { - for (int32_t i = 0; i < num_significant_digits && (str_out - str) < 36; i++) { - *(str_out++) = *(significand_read++) + '0'; - } - *str_out = '\0'; - } else { - int32_t radix_position = num_significant_digits + exponent; - - // Reserve space for null terminator. - const int available_bytes = BSON_DECIMAL128_STRING - 1; - const char *const str_end = str + available_bytes; - - if (radix_position > 0) { /* non-zero digits before radix */ - for (int32_t i = 0; i < radix_position && str_out != str_end; i++) { - *(str_out++) = *(significand_read++) + '0'; - } - } else { /* leading zero before radix point */ - *(str_out++) = '0'; - } - - *(str_out++) = '.'; - while (radix_position++ < 0 && str_out != str_end) { /* add leading zeros after radix */ - *(str_out++) = '0'; - } - - const unsigned dot_pos = (unsigned)BSON_MAX(radix_position - 1, 0); - const unsigned n_trailing_digits = num_significant_digits - dot_pos; - const unsigned n_to_write = BSON_MIN(n_trailing_digits, (unsigned)(str_end - str_out)); - mlib_foreach_urange (i, n_to_write) { - (void)i; - *str_out++ = *significand_read++ + '0'; - } - *str_out = '\0'; - } - } -} - -typedef struct { - uint64_t high, low; -} _bson_uint128_6464_t; - - -/** - *------------------------------------------------------------------------- - * - * mul64x64 -- - * - * This function multiplies two &uint64_t into a &_bson_uint128_6464_t. - * - * Returns: - * The product of @left and @right. - * - * Side Effects: - * None. - * - *------------------------------------------------------------------------- - */ -static void -_mul_64x64(uint64_t left, /* IN */ - uint64_t right, /* IN */ - _bson_uint128_6464_t *product) /* OUT */ -{ - uint64_t left_high, left_low, right_high, right_low, product_high, product_mid, product_mid2, product_low; - _bson_uint128_6464_t rt = {0}; - - if (!left && !right) { - *product = rt; - return; - } - - left_high = left >> 32; - left_low = (uint32_t)left; - right_high = right >> 32; - right_low = (uint32_t)right; - - product_high = left_high * right_high; - product_mid = left_high * right_low; - product_mid2 = left_low * right_high; - product_low = left_low * right_low; - - product_high += product_mid >> 32; - product_mid = (uint32_t)product_mid + product_mid2 + (product_low >> 32); - - product_high = product_high + (product_mid >> 32); - product_low = (product_mid << 32) + (uint32_t)product_low; - - rt.high = product_high; - rt.low = product_low; - *product = rt; -} - -/** - *------------------------------------------------------------------------------ - * - * _dec128_tolower -- - * - * This function converts the ASCII character @c to lowercase. It is locale - * insensitive (unlike the stdlib tolower). - * - * Returns: - * The lowercased character. - */ -char -_dec128_tolower(char c) -{ - if (isupper(c)) { - c += 32; - } - - return c; -} - -/** - *------------------------------------------------------------------------------ - * - * _dec128_istreq -- - * - * This function compares the null-terminated *ASCII* strings @a and @b - * for case-insensitive equality. - * - * Returns: - * true if the strings are equal, false otherwise. - */ -bool -_dec128_istreq(const char *a, /* IN */ - const char *b /* IN */) -{ - while (*a != '\0' || *b != '\0') { - /* strings are different lengths. */ - if (*a == '\0' || *b == '\0') { - return false; - } - - if (_dec128_tolower(*a) != _dec128_tolower(*b)) { - return false; - } - - a++; - b++; - } - - return true; -} - -/** - *------------------------------------------------------------------------------ - * - * bson_decimal128_from_string -- - * - * This function converts @string in the format [+-]ddd[.]ddd[E][+-]dddd to - * decimal128. Out of range values are converted to +/-Infinity. Invalid - * strings are converted to NaN. - * - * If more digits are provided than the available precision allows, - * round to the nearest expressable decimal128 with ties going to even will - * occur. - * - * Note: @string must be ASCII only! - * - * Returns: - * true on success, or false on failure. @dec will be NaN if @str was invalid - * The &bson_decimal128_t converted from @string at @dec. - * - * Side effects: - * None. - * - *------------------------------------------------------------------------------ - */ -bool -bson_decimal128_from_string(const char *string, /* IN */ - bson_decimal128_t *dec) /* OUT */ -{ - return bson_decimal128_from_string_w_len(string, -1, dec); -} - - -/** - *------------------------------------------------------------------------------ - * - * bson_decimal128_from_string_w_len -- - * - * This function converts @string in the format [+-]ddd[.]ddd[E][+-]dddd to - * decimal128. Out of range values are converted to +/-Infinity. Invalid - * strings are converted to NaN. @len is the length of the string, or -1 - * meaning the string is null-terminated. - * - * If more digits are provided than the available precision allows, - * round to the nearest expressable decimal128 with ties going to even will - * occur. - * - * Note: @string must be ASCII only! - * - * Returns: - * true on success, or false on failure. @dec will be NaN if @str was invalid - * The &bson_decimal128_t converted from @string at @dec. - * - * Side effects: - * None. - * - *------------------------------------------------------------------------------ - */ -bool -bson_decimal128_from_string_w_len(const char *string, /* IN */ - int len, /* IN */ - bson_decimal128_t *dec) /* OUT */ -{ - _bson_uint128_6464_t significand = {0}; - - const char *str_read = string; /* Read pointer for consuming str. */ - - /* Parsing state tracking */ - bool is_negative = false; - bool saw_radix = false; - bool includes_sign = false; /* True if the input string contains a sign. */ - bool found_nonzero = false; - - size_t significant_digits = 0; /* Total number of significant digits - * (no leading or trailing zero) */ - size_t ndigits_read = 0; /* Total number of significand digits read */ - size_t ndigits = 0; /* Total number of digits (no leading zeros) */ - size_t radix_position = 0; /* The number of the digits after radix */ - size_t first_nonzero = 0; /* The index of the first non-zero in *str* */ - - uint16_t digits[BSON_DECIMAL128_MAX_DIGITS] = {0}; - uint16_t ndigits_stored = 0; /* The number of digits in digits */ - uint16_t *digits_insert = digits; /* Insertion pointer for digits */ - size_t first_digit = 0; /* The index of the first non-zero digit */ - size_t last_digit = 0; /* The index of the last digit */ - - int32_t exponent = 0; - uint64_t significand_high = 0; /* The high 17 digits of the significand */ - uint64_t significand_low = 0; /* The low 17 digits of the significand */ - uint16_t biased_exponent = 0; /* The biased exponent */ - - BSON_ASSERT(dec); - dec->high = 0; - dec->low = 0; - - if (*str_read == '+' || *str_read == '-') { - is_negative = *(str_read++) == '-'; - includes_sign = true; - } - - /* Check for Infinity or NaN */ - if (!isdigit(*str_read) && *str_read != '.') { - if (_dec128_istreq(str_read, "inf") || _dec128_istreq(str_read, "infinity")) { - BSON_DECIMAL128_SET_INF(*dec, is_negative); - return true; - } else if (_dec128_istreq(str_read, "nan")) { - BSON_DECIMAL128_SET_NAN(*dec); - return true; - } - - BSON_DECIMAL128_SET_NAN(*dec); - return false; - } - - /* Read digits */ - while (((isdigit(*str_read) || *str_read == '.')) && (len == -1 || str_read < string + len)) { - if (*str_read == '.') { - if (saw_radix) { - BSON_DECIMAL128_SET_NAN(*dec); - return false; - } - - saw_radix = true; - str_read++; - continue; - } - - if (ndigits_stored < BSON_DECIMAL128_MAX_DIGITS) { - if (*str_read != '0' || found_nonzero) { - if (!found_nonzero) { - first_nonzero = ndigits_read; - } - - found_nonzero = true; - *(digits_insert++) = (uint16_t)(*(str_read) - '0'); /* Only store 34 digits */ - ndigits_stored++; - } - } - - if (found_nonzero) { - ndigits++; - } - - if (saw_radix) { - radix_position++; - } - - ndigits_read++; - str_read++; - } - - if (saw_radix && !ndigits_read) { - BSON_DECIMAL128_SET_NAN(*dec); - return false; - } - - /* Read exponent if exists */ - if (*str_read == 'e' || *str_read == 'E') { - int nread = 0; -#ifdef _MSC_VER -#define SSCANF sscanf_s -#else -#define SSCANF sscanf -#endif - int64_t temp_exponent = 0; - int read_exponent = SSCANF(++str_read, "%" SCNd64 "%n", &temp_exponent, &nread); - str_read += nread; - - if (!read_exponent || nread == 0 || !mlib_in_range(int32_t, temp_exponent)) { - BSON_DECIMAL128_SET_NAN(*dec); - return false; - } - - exponent = (int32_t)temp_exponent; -#undef SSCANF - } - - if ((len == -1 || str_read < string + len) && *str_read) { - BSON_DECIMAL128_SET_NAN(*dec); - return false; - } - - /* Done reading input. */ - /* Find first non-zero digit in digits */ - first_digit = 0; - - if (!ndigits_stored) { /* value is zero */ - last_digit = 0; - digits[0] = 0; - ndigits = 1; - ndigits_stored = 1; - significant_digits = 0; - } else { - last_digit = ndigits_stored - 1; - significant_digits = ndigits; - /* Mark trailing zeros as non-significant */ - while (string[first_nonzero + significant_digits - 1 + includes_sign + saw_radix] == '0') { - significant_digits--; - } - } - - - /* Normalization of exponent */ - /* Correct exponent based on radix position, and shift significand as needed - */ - /* to represent user input */ - - /* Overflow prevention */ - if (mlib_cmp(exponent, <=, radix_position) && mlib_cmp(radix_position, >, exponent + (1 << 14))) { - exponent = BSON_DECIMAL128_EXPONENT_MIN; - } else { - BSON_ASSERT(mlib_in_range(int32_t, radix_position)); - exponent -= (int32_t)radix_position; - } - - /* Attempt to normalize the exponent */ - while (exponent > BSON_DECIMAL128_EXPONENT_MAX) { - /* Shift exponent to significand and decrease */ - last_digit++; - - 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. */ - exponent = BSON_DECIMAL128_EXPONENT_MAX; - break; - } - - /* Overflow is not permitted, error. */ - BSON_DECIMAL128_SET_NAN(*dec); - return false; - } - - exponent--; - } - - while (exponent < BSON_DECIMAL128_EXPONENT_MIN || ndigits_stored < ndigits) { - /* Shift last digit */ - if (last_digit == 0) { - /* underflow is not allowed, but zero clamping is */ - if (significant_digits == 0) { - exponent = BSON_DECIMAL128_EXPONENT_MIN; - break; - } - - BSON_DECIMAL128_SET_NAN(*dec); - return false; - } - - if (ndigits_stored < ndigits) { - if (string[ndigits - 1 + includes_sign + saw_radix] - '0' != 0 && significant_digits != 0) { - BSON_DECIMAL128_SET_NAN(*dec); - return false; - } - - ndigits--; /* adjust to match digits not stored */ - } else { - if (digits[last_digit] != 0) { - /* Inexact rounding is not allowed. */ - BSON_DECIMAL128_SET_NAN(*dec); - return false; - } - - - last_digit--; /* adjust to round */ - } - - if (exponent < BSON_DECIMAL128_EXPONENT_MAX) { - exponent++; - } else { - BSON_DECIMAL128_SET_NAN(*dec); - return false; - } - } - - /* Round */ - /* We've normalized the exponent, but might still need to round. */ - if (last_digit - first_digit + 1 < significant_digits) { - /* There are non-zero digits after last_digit that need rounding. */ - /* We round to nearest, ties to even */ - const char round_digit = string[first_nonzero + last_digit + includes_sign + saw_radix + 1] - '0'; - - if (round_digit != 0) { - /* Inexact (non-zero) rounding is not allowed */ - BSON_DECIMAL128_SET_NAN(*dec); - return false; - } - } - - /* Encode significand */ - - if (significant_digits == 0) { /* read a zero */ - significand_high = 0; - significand_low = 0; - } else if (last_digit - first_digit < 17) { - size_t d_idx = first_digit; - significand_low = digits[d_idx++]; - - for (; d_idx <= last_digit; d_idx++) { - significand_low *= 10; - significand_low += digits[d_idx]; - significand_high = 0; - } - } else { - size_t d_idx = first_digit; - significand_high = digits[d_idx++]; - - for (; d_idx <= last_digit - 17; d_idx++) { - significand_high *= 10; - significand_high += digits[d_idx]; - } - - significand_low = digits[d_idx++]; - - for (; d_idx <= last_digit; d_idx++) { - significand_low *= 10; - significand_low += digits[d_idx]; - } - } - - _mul_64x64(significand_high, 100000000000000000ull, &significand); - significand.low += significand_low; - - if (significand.low < significand_low) { - significand.high += 1; - } - - - biased_exponent = (exponent + (int16_t)BSON_DECIMAL128_EXPONENT_BIAS); - - /* Encode combination, exponent, and significand. */ - if ((significand.high >> 49) & 1) { - /* Encode '11' into bits 1 to 3 */ - dec->high |= (0x3ull << 61); - dec->high |= (biased_exponent & 0x3fffull) << 47; - dec->high |= significand.high & 0x7fffffffffffull; - } else { - dec->high |= (biased_exponent & 0x3fffull) << 49; - dec->high |= significand.high & 0x1ffffffffffffull; - } - - dec->low = significand.low; - - /* Encode sign */ - if (is_negative) { - dec->high |= 0x8000000000000000ull; - } - - return true; -} diff --git a/bsonjs/bson/bson-decimal128.h b/bsonjs/bson/bson-decimal128.h deleted file mode 100644 index ddb9bb8..0000000 --- a/bsonjs/bson/bson-decimal128.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_DECIMAL128_H -#define BSON_DECIMAL128_H - - -#include -#include -#include - -#include - - -/** - * BSON_DECIMAL128_STRING: - * - * The length of a decimal128 string (with null terminator). - * - * 1 for the sign - * 35 for digits and radix - * 2 for exponent indicator and sign - * 4 for exponent digits - */ -#define BSON_DECIMAL128_STRING 43 -#define BSON_DECIMAL128_INF "Infinity" -#define BSON_DECIMAL128_NAN "NaN" - - -BSON_BEGIN_DECLS - -BSON_EXPORT(void) -bson_decimal128_to_string(const bson_decimal128_t *dec, char *str); - - -/* Note: @string must be ASCII characters only! */ -BSON_EXPORT(bool) -bson_decimal128_from_string(const char *string, bson_decimal128_t *dec); - -BSON_EXPORT(bool) -bson_decimal128_from_string_w_len(const char *string, int len, bson_decimal128_t *dec); - -BSON_END_DECLS - - -#endif /* BSON_DECIMAL128_H */ diff --git a/bsonjs/bson/bson-endian.h b/bsonjs/bson/bson-endian.h deleted file mode 100644 index 2830cdf..0000000 --- a/bsonjs/bson/bson-endian.h +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_ENDIAN_H -#define BSON_ENDIAN_H - -#include -#include -#include - - -BSON_BEGIN_DECLS - - -#define BSON_BIG_ENDIAN 4321 -#define BSON_LITTLE_ENDIAN 1234 - -#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__) && (__clang_major__ >= 3) && \ - (__clang_minor__ >= 1) -#if __has_builtin(__builtin_bswap16) -#define BSON_UINT16_SWAP_LE_BE(v) __builtin_bswap16(v) -#endif -#if __has_builtin(__builtin_bswap32) -#define BSON_UINT32_SWAP_LE_BE(v) __builtin_bswap32(v) -#endif -#if __has_builtin(__builtin_bswap64) -#define BSON_UINT64_SWAP_LE_BE(v) __builtin_bswap64(v) -#endif -#elif defined(__GNUC__) && (__GNUC__ >= 4) -#if __GNUC__ > 4 || (defined(__GNUC_MINOR__) && __GNUC_MINOR__ >= 3) -#define BSON_UINT32_SWAP_LE_BE(v) __builtin_bswap32((uint32_t)v) -#define BSON_UINT64_SWAP_LE_BE(v) __builtin_bswap64((uint64_t)v) -#endif -#if __GNUC__ > 4 || (defined(__GNUC_MINOR__) && __GNUC_MINOR__ >= 8) -#define BSON_UINT16_SWAP_LE_BE(v) __builtin_bswap16((uint32_t)v) -#endif -#endif - - -#ifndef BSON_UINT16_SWAP_LE_BE -#define BSON_UINT16_SWAP_LE_BE(v) __bson_uint16_swap_slow((uint16_t)v) -#endif - - -#ifndef BSON_UINT32_SWAP_LE_BE -#define BSON_UINT32_SWAP_LE_BE(v) __bson_uint32_swap_slow((uint32_t)v) -#endif - - -#ifndef BSON_UINT64_SWAP_LE_BE -#define BSON_UINT64_SWAP_LE_BE(v) __bson_uint64_swap_slow((uint64_t)v) -#endif - - -#if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN -#define BSON_UINT16_FROM_LE(v) ((uint16_t)v) -#define BSON_UINT16_TO_LE(v) ((uint16_t)v) -#define BSON_UINT16_FROM_BE(v) BSON_UINT16_SWAP_LE_BE(v) -#define BSON_UINT16_TO_BE(v) BSON_UINT16_SWAP_LE_BE(v) -#define BSON_UINT32_FROM_LE(v) ((uint32_t)v) -#define BSON_UINT32_TO_LE(v) ((uint32_t)v) -#define BSON_UINT32_FROM_BE(v) BSON_UINT32_SWAP_LE_BE(v) -#define BSON_UINT32_TO_BE(v) BSON_UINT32_SWAP_LE_BE(v) -#define BSON_UINT64_FROM_LE(v) ((uint64_t)v) -#define BSON_UINT64_TO_LE(v) ((uint64_t)v) -#define BSON_UINT64_FROM_BE(v) BSON_UINT64_SWAP_LE_BE(v) -#define BSON_UINT64_TO_BE(v) BSON_UINT64_SWAP_LE_BE(v) -#define BSON_DOUBLE_FROM_LE(v) ((double)v) -#define BSON_DOUBLE_TO_LE(v) ((double)v) -#define BSON_FLOAT_FROM_LE(v) ((float)v) -#define BSON_FLOAT_TO_LE(v) ((float)v) -#elif BSON_BYTE_ORDER == BSON_BIG_ENDIAN -#define BSON_UINT16_FROM_LE(v) BSON_UINT16_SWAP_LE_BE(v) -#define BSON_UINT16_TO_LE(v) BSON_UINT16_SWAP_LE_BE(v) -#define BSON_UINT16_FROM_BE(v) ((uint16_t)v) -#define BSON_UINT16_TO_BE(v) ((uint16_t)v) -#define BSON_UINT32_FROM_LE(v) BSON_UINT32_SWAP_LE_BE(v) -#define BSON_UINT32_TO_LE(v) BSON_UINT32_SWAP_LE_BE(v) -#define BSON_UINT32_FROM_BE(v) ((uint32_t)v) -#define BSON_UINT32_TO_BE(v) ((uint32_t)v) -#define BSON_UINT64_FROM_LE(v) BSON_UINT64_SWAP_LE_BE(v) -#define BSON_UINT64_TO_LE(v) BSON_UINT64_SWAP_LE_BE(v) -#define BSON_UINT64_FROM_BE(v) ((uint64_t)v) -#define BSON_UINT64_TO_BE(v) ((uint64_t)v) -#define BSON_DOUBLE_FROM_LE(v) (__bson_double_swap_slow(v)) -#define BSON_DOUBLE_TO_LE(v) (__bson_double_swap_slow(v)) -#define BSON_FLOAT_FROM_LE(v) (__bson_float_swap_slow(v)) -#define BSON_FLOAT_TO_LE(v) (__bson_float_swap_slow(v)) -#else -#error "The endianness of target architecture is unknown." -#endif - - -/* - *-------------------------------------------------------------------------- - * - * __bson_uint16_swap_slow -- - * - * Fallback endianness conversion for 16-bit integers. - * - * Returns: - * The endian swapped version. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static BSON_INLINE uint16_t -__bson_uint16_swap_slow(uint16_t v) /* IN */ -{ - return (uint16_t)((v & 0x00FF) << 8) | (uint16_t)((v & 0xFF00) >> 8); -} - - -/* - *-------------------------------------------------------------------------- - * - * __bson_uint32_swap_slow -- - * - * Fallback endianness conversion for 32-bit integers. - * - * Returns: - * The endian swapped version. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static BSON_INLINE uint32_t -__bson_uint32_swap_slow(uint32_t v) /* IN */ -{ - return ((v & 0x000000FFU) << 24) | ((v & 0x0000FF00U) << 8) | ((v & 0x00FF0000U) >> 8) | ((v & 0xFF000000U) >> 24); -} - - -/* - *-------------------------------------------------------------------------- - * - * __bson_uint64_swap_slow -- - * - * Fallback endianness conversion for 64-bit integers. - * - * Returns: - * The endian swapped version. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static BSON_INLINE uint64_t -__bson_uint64_swap_slow(uint64_t v) /* IN */ -{ - return ((v & 0x00000000000000FFULL) << 56) | ((v & 0x000000000000FF00ULL) << 40) | - ((v & 0x0000000000FF0000ULL) << 24) | ((v & 0x00000000FF000000ULL) << 8) | - ((v & 0x000000FF00000000ULL) >> 8) | ((v & 0x0000FF0000000000ULL) >> 24) | - ((v & 0x00FF000000000000ULL) >> 40) | ((v & 0xFF00000000000000ULL) >> 56); -} - - -/* - *-------------------------------------------------------------------------- - * - * __bson_double_swap_slow -- - * - * Fallback endianness conversion for double floating point. - * - * Returns: - * The endian swapped version. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -BSON_STATIC_ASSERT2(sizeof_uint64_t, sizeof(double) == sizeof(uint64_t)); - -static BSON_INLINE double -__bson_double_swap_slow(double v) /* IN */ -{ - uint64_t uv; - - memcpy(&uv, &v, sizeof(v)); - uv = BSON_UINT64_SWAP_LE_BE(uv); - memcpy(&v, &uv, sizeof(v)); - - return v; -} - - -/* - *-------------------------------------------------------------------------- - * - * __bson_float_swap_slow -- - * - * Fallback endianness conversion for single floating point. - * - * Returns: - * The endian swapped version. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -BSON_STATIC_ASSERT2(sizeof_uint32_t, sizeof(float) == sizeof(uint32_t)); - -static BSON_INLINE float -__bson_float_swap_slow(float v) /* IN */ -{ - uint32_t uv; - - memcpy(&uv, &v, sizeof(v)); - uv = BSON_UINT32_SWAP_LE_BE(uv); - memcpy(&v, &uv, sizeof(v)); - - return v; -} - -BSON_END_DECLS - - -#endif /* BSON_ENDIAN_H */ diff --git a/bsonjs/bson/bson-error-private.h b/bsonjs/bson/bson-error-private.h deleted file mode 100644 index 32f4f6b..0000000 --- a/bsonjs/bson/bson-error-private.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#ifndef BSON_ERROR_PRIVATE_H -#define BSON_ERROR_PRIVATE_H - -#include // IWYU pragma: export - -// - -#include - - -#define BSON_ERROR_CATEGORY 1 - - -static BSON_INLINE void -bson_set_error_category(bson_error_t *error, uint8_t category) -{ - BSON_ASSERT_PARAM(error); - error->reserved = category; -} - -#endif /* BSON_ERROR_PRIVATE_H */ diff --git a/bsonjs/bson/bson-iso8601-private.h b/bsonjs/bson/bson-iso8601-private.h deleted file mode 100644 index 0ca5e47..0000000 --- a/bsonjs/bson/bson-iso8601-private.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_ISO8601_PRIVATE_H -#define BSON_ISO8601_PRIVATE_H - - -#include - -#include -#include - - -BSON_BEGIN_DECLS - -bool -_bson_iso8601_date_parse(const char *str, int32_t len, int64_t *out, bson_error_t *error); - -BSON_END_DECLS - - -#endif /* BSON_ISO8601_PRIVATE_H */ diff --git a/bsonjs/bson/bson-iso8601.c b/bsonjs/bson/bson-iso8601.c deleted file mode 100644 index 8175e96..0000000 --- a/bsonjs/bson/bson-iso8601.c +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include -#include - -#include -#include -#include -#include - - -static bool -get_tok(const char *terminals, const char **ptr, int32_t *remaining, const char **out, int32_t *out_len) -{ - const char *terminal; - bool found_terminal = false; - - if (!*remaining) { - *out = ""; - *out_len = 0; - } - - *out = *ptr; - *out_len = -1; - - for (; *remaining && !found_terminal; (*ptr)++, (*remaining)--, (*out_len)++) { - for (terminal = terminals; *terminal; terminal++) { - if (**ptr == *terminal) { - found_terminal = true; - break; - } - } - } - - if (!found_terminal) { - (*out_len)++; - } - - return found_terminal; -} - -static bool -digits_only(const char *str, int32_t len) -{ - int i; - - for (i = 0; i < len; i++) { - if (!isdigit(str[i])) { - return false; - } - } - - return true; -} - -static bool -parse_num(const char *str, int32_t len, int32_t digits, int32_t min, int32_t max, int32_t *out) -{ - int i; - int magnitude = 1; - int32_t value = 0; - - if ((digits >= 0 && len != digits) || !digits_only(str, len)) { - return false; - } - - for (i = 1; i <= len; i++, magnitude *= 10) { - value += (str[len - i] - '0') * magnitude; - } - - if (value < min || value > max) { - return false; - } - - *out = value; - - return true; -} - -bool -_bson_iso8601_date_parse(const char *str, int32_t len, int64_t *out, bson_error_t *error) -{ - const char *ptr; - int32_t remaining = len; - - const char *year_ptr = NULL; - const char *month_ptr = NULL; - const char *day_ptr = NULL; - const char *hour_ptr = NULL; - const char *min_ptr = NULL; - const char *sec_ptr = NULL; - const char *millis_ptr = NULL; - const char *tz_ptr = NULL; - - int32_t year_len = 0; - int32_t month_len = 0; - int32_t day_len = 0; - int32_t hour_len = 0; - int32_t min_len = 0; - int32_t sec_len = 0; - int32_t millis_len = 0; - int32_t tz_len = 0; - - int32_t year; - int32_t month; - int32_t day; - int32_t hour; - int32_t min; - int32_t sec = 0; - int64_t millis = 0; - int32_t tz_adjustment = 0; - - struct bson_tm posix_date = {0}; - -#define DATE_PARSE_ERR(msg) \ - bson_set_error( \ - error, BSON_ERROR_JSON, BSON_JSON_ERROR_READ_INVALID_PARAM, "Could not parse \"%s\" as date: " msg, str); \ - return false - -#define DEFAULT_DATE_PARSE_ERR \ - DATE_PARSE_ERR("use ISO8601 format yyyy-mm-ddThh:mm plus timezone, either" \ - " \"Z\" or like \"+0500\" or like \"+05:00\"") - - ptr = str; - - /* we have to match at least yyyy-mm-ddThh:mm */ - if (!(get_tok("-", &ptr, &remaining, &year_ptr, &year_len) && - get_tok("-", &ptr, &remaining, &month_ptr, &month_len) && get_tok("T", &ptr, &remaining, &day_ptr, &day_len) && - get_tok(":", &ptr, &remaining, &hour_ptr, &hour_len) && - get_tok(":+-Z", &ptr, &remaining, &min_ptr, &min_len))) { - DEFAULT_DATE_PARSE_ERR; - } - - /* if the minute has a ':' at the end look for seconds */ - if (min_ptr[min_len] == ':') { - if (remaining < 2) { - DATE_PARSE_ERR("reached end of date while looking for seconds"); - } - - get_tok(".+-Z", &ptr, &remaining, &sec_ptr, &sec_len); - - if (!sec_len) { - DATE_PARSE_ERR("minute ends in \":\" seconds is required"); - } - } - - /* if we had a second and it is followed by a '.' look for milliseconds */ - if (sec_len && sec_ptr[sec_len] == '.') { - if (remaining < 2) { - DATE_PARSE_ERR("reached end of date while looking for milliseconds"); - } - - get_tok("+-Z", &ptr, &remaining, &millis_ptr, &millis_len); - - if (!millis_len) { - DATE_PARSE_ERR("seconds ends in \".\", milliseconds is required"); - } - } - - /* backtrack by 1 to put ptr on the timezone */ - ptr--; - remaining++; - - get_tok("", &ptr, &remaining, &tz_ptr, &tz_len); - - if (!parse_num(year_ptr, year_len, 4, -9999, 9999, &year)) { - DATE_PARSE_ERR("year must be an integer"); - } - - /* values are as in struct tm */ - year -= 1900; - - if (!parse_num(month_ptr, month_len, 2, 1, 12, &month)) { - DATE_PARSE_ERR("month must be an integer"); - } - - /* values are as in struct tm */ - month -= 1; - - if (!parse_num(day_ptr, day_len, 2, 1, 31, &day)) { - DATE_PARSE_ERR("day must be an integer"); - } - - if (!parse_num(hour_ptr, hour_len, 2, 0, 23, &hour)) { - DATE_PARSE_ERR("hour must be an integer"); - } - - if (!parse_num(min_ptr, min_len, 2, 0, 59, &min)) { - DATE_PARSE_ERR("minute must be an integer"); - } - - if (sec_len && !parse_num(sec_ptr, sec_len, 2, 0, 60, &sec)) { - DATE_PARSE_ERR("seconds must be an integer"); - } - - if (tz_len > 0) { - if (tz_ptr[0] == 'Z' && tz_len == 1) { - /* valid */ - } else if (tz_ptr[0] == '+' || tz_ptr[0] == '-') { - int32_t tz_hour; - int32_t tz_min; - - if ((tz_len != 5 || !digits_only(tz_ptr + 1, 4)) && - (tz_len != 6 || !digits_only(tz_ptr + 1, 2) || tz_ptr[3] != ':' || !digits_only(tz_ptr + 4, 2))) { - DATE_PARSE_ERR("could not parse timezone"); - } - - if (!parse_num(tz_ptr + 1, 2, -1, -23, 23, &tz_hour)) { - DATE_PARSE_ERR("timezone hour must be at most 23"); - } - - int32_t tz_min_offset = tz_ptr[3] == ':' ? 1 : 0; - if (!parse_num(tz_ptr + 3 + tz_min_offset, 2, -1, 0, 59, &tz_min)) { - DATE_PARSE_ERR("timezone minute must be at most 59"); - } - - /* we inflect the meaning of a 'positive' timezone. Those are hours - * we have to subtract, and vice versa */ - tz_adjustment = (tz_ptr[0] == '-' ? 1 : -1) * ((tz_min * 60) + (tz_hour * 60 * 60)); - - if (!(tz_adjustment > -86400 && tz_adjustment < 86400)) { - DATE_PARSE_ERR("timezone offset must be less than 24 hours"); - } - } else { - DATE_PARSE_ERR("timezone is required"); - } - } - - if (millis_len > 0) { - int i; - int magnitude; - millis = 0; - - if (millis_len > 3 || !digits_only(millis_ptr, millis_len)) { - DATE_PARSE_ERR("milliseconds must be an integer"); - } - - for (i = 1, magnitude = 1; i <= millis_len; i++, magnitude *= 10) { - millis += (millis_ptr[millis_len - i] - '0') * magnitude; - } - - if (millis_len == 1) { - millis *= 100; - } else if (millis_len == 2) { - millis *= 10; - } - - if (millis < 0 || millis > 1000) { - DATE_PARSE_ERR("milliseconds must be at least 0 and less than 1000"); - } - } - - posix_date.tm_sec = sec; - posix_date.tm_min = min; - posix_date.tm_hour = hour; - posix_date.tm_mday = day; - posix_date.tm_mon = month; - posix_date.tm_year = year; - posix_date.tm_wday = 0; - posix_date.tm_yday = 0; - - millis = 1000 * _bson_timegm(&posix_date) + millis; - millis += tz_adjustment * 1000; - *out = millis; - - return true; -} diff --git a/bsonjs/bson/bson-iter.c b/bsonjs/bson/bson-iter.c deleted file mode 100644 index cec8730..0000000 --- a/bsonjs/bson/bson-iter.c +++ /dev/null @@ -1,2625 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include - -#include -#include -#include - -#include -#include - -#define ITER_TYPE(i) ((bson_type_t) * ((i)->raw + (i)->type)) - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_init -- - * - * Initializes @iter to be used to iterate @bson. - * - * Returns: - * true if bson_iter_t was initialized. otherwise false. - * - * Side effects: - * @iter is initialized. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_iter_init(bson_iter_t *iter, /* OUT */ - const bson_t *bson) /* IN */ -{ - BSON_ASSERT(iter); - BSON_ASSERT(bson); - - if (BSON_UNLIKELY(bson->len < 5)) { - memset(iter, 0, sizeof *iter); - return false; - } - - iter->raw = bson_get_data(bson); - iter->len = bson->len; - iter->off = 0; - iter->type = 0; - iter->key = 0; - iter->d1 = 0; - iter->d2 = 0; - iter->d3 = 0; - iter->d4 = 0; - iter->next_off = 4; - iter->err_off = 0; - iter->value = (bson_value_t){0}; - - return true; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_init_from_data -- - * - * Initializes @iter to be used to iterate @data of length @length - * - * Returns: - * true if bson_iter_t was initialized. otherwise false. - * - * Side effects: - * @iter is initialized. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_iter_init_from_data(bson_iter_t *iter, /* OUT */ - const uint8_t *data, /* IN */ - size_t length) /* IN */ -{ - BSON_ASSERT(iter); - BSON_ASSERT(data); - - if (BSON_UNLIKELY((length < 5) || (length > INT_MAX))) { - memset(iter, 0, sizeof *iter); - return false; - } - - // Check that the object header matches the length of the given buffer - const uint32_t hdr_len = mlib_read_u32le(data); - if (BSON_UNLIKELY(hdr_len != length)) { - memset(iter, 0, sizeof *iter); - return false; - } - - if (BSON_UNLIKELY(data[length - 1])) { - memset(iter, 0, sizeof *iter); - return false; - } - - if (BSON_UNLIKELY(!mlib_in_range(uint32_t, length))) { - memset(iter, 0, sizeof *iter); - return false; - } - - iter->raw = (uint8_t *)data; - iter->len = (uint32_t)length; - iter->off = 0; - iter->type = 0; - iter->key = 0; - iter->d1 = 0; - iter->d2 = 0; - iter->d3 = 0; - iter->d4 = 0; - iter->next_off = 4; - iter->err_off = 0; - - return true; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_recurse -- - * - * Creates a new sub-iter looking at the document or array that @iter - * is currently pointing at. - * - * Returns: - * true if successful and @child was initialized. - * - * Side effects: - * @child is initialized. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_iter_recurse(const bson_iter_t *iter, /* IN */ - bson_iter_t *child) /* OUT */ -{ - const uint8_t *data = NULL; - uint32_t len = 0; - - BSON_ASSERT(iter); - BSON_ASSERT(child); - - if (ITER_TYPE(iter) == BSON_TYPE_DOCUMENT) { - bson_iter_document(iter, &len, &data); - } else if (ITER_TYPE(iter) == BSON_TYPE_ARRAY) { - bson_iter_array(iter, &len, &data); - } else { - return false; - } - - child->raw = data; - child->len = len; - child->off = 0; - child->type = 0; - child->key = 0; - child->d1 = 0; - child->d2 = 0; - child->d3 = 0; - child->d4 = 0; - child->next_off = 4; - child->err_off = 0; - - return true; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_init_find -- - * - * Initializes a #bson_iter_t and moves the iter to the first field - * matching @key. - * - * Returns: - * true if the field named @key was found; otherwise false. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_iter_init_find(bson_iter_t *iter, /* INOUT */ - const bson_t *bson, /* IN */ - const char *key) /* IN */ -{ - BSON_ASSERT(iter); - BSON_ASSERT(bson); - BSON_ASSERT(key); - - return bson_iter_init(iter, bson) && bson_iter_find(iter, key); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_init_find_w_len -- - * - * Initializes a #bson_iter_t and moves the iter to the first field - * matching @key. - * - * Returns: - * true if the field named @key was found; otherwise false. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_iter_init_find_w_len(bson_iter_t *iter, /* INOUT */ - const bson_t *bson, /* IN */ - const char *key, /* IN */ - int keylen) /* IN */ -{ - BSON_ASSERT(iter); - BSON_ASSERT(bson); - BSON_ASSERT(key); - - return bson_iter_init(iter, bson) && bson_iter_find_w_len(iter, key, keylen); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_init_find_case -- - * - * A case-insensitive version of bson_iter_init_find(). - * - * Returns: - * true if the field was found and @iter is observing that field. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_iter_init_find_case(bson_iter_t *iter, /* INOUT */ - const bson_t *bson, /* IN */ - const char *key) /* IN */ -{ - BSON_ASSERT(iter); - BSON_ASSERT(bson); - BSON_ASSERT(key); - - return bson_iter_init(iter, bson) && bson_iter_find_case(iter, key); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_find_w_len -- - * - * Searches through @iter starting from the current position for a key - * matching @key. @keylen indicates the length of @key, or -1 to - * determine the length with strlen(). - * - * Returns: - * true if the field @key was found. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_iter_find_w_len(bson_iter_t *iter, /* INOUT */ - const char *key, /* IN */ - int keylen) /* IN */ -{ - const char *ikey; - - if (keylen < 0) { - keylen = (int)strlen(key); - } - - while (bson_iter_next(iter)) { - ikey = bson_iter_key(iter); - - if ((0 == strncmp(key, ikey, keylen)) && (ikey[keylen] == '\0')) { - return true; - } - } - - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_find -- - * - * Searches through @iter starting from the current position for a key - * matching @key. This is a case-sensitive search meaning "KEY" and - * "key" would NOT match. - * - * Returns: - * true if @key is found. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_iter_find(bson_iter_t *iter, /* INOUT */ - const char *key) /* IN */ -{ - BSON_ASSERT(iter); - BSON_ASSERT(key); - - return bson_iter_find_w_len(iter, key, -1); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_find_case -- - * - * Searches through @iter starting from the current position for a key - * matching @key. This is a case-insensitive search meaning "KEY" and - * "key" would match. - * - * Returns: - * true if @key is found. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_iter_find_case(bson_iter_t *iter, /* INOUT */ - const char *key) /* IN */ -{ - BSON_ASSERT(iter); - BSON_ASSERT(key); - - while (bson_iter_next(iter)) { - if (!bson_strcasecmp(key, bson_iter_key(iter))) { - return true; - } - } - - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_find_descendant -- - * - * Locates a descendant using the "parent.child.key" notation. This - * operates similar to bson_iter_find() except that it can recurse - * into children documents using the dot notation. - * - * Returns: - * true if the descendant was found and @descendant was initialized. - * - * Side effects: - * @descendant may be initialized. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_iter_find_descendant(bson_iter_t *iter, /* INOUT */ - const char *dotkey, /* IN */ - bson_iter_t *descendant) /* OUT */ -{ - bson_iter_t tmp; - const char *dot; - size_t sublen; - - BSON_ASSERT(iter); - BSON_ASSERT(dotkey); - BSON_ASSERT(descendant); - - if ((dot = strchr(dotkey, '.'))) { - sublen = dot - dotkey; - } else { - sublen = strlen(dotkey); - } - - if (bson_iter_find_w_len(iter, dotkey, (int)sublen)) { - if (!dot) { - *descendant = *iter; - return true; - } - - if (BSON_ITER_HOLDS_DOCUMENT(iter) || BSON_ITER_HOLDS_ARRAY(iter)) { - if (bson_iter_recurse(iter, &tmp)) { - return bson_iter_find_descendant(&tmp, dot + 1, descendant); - } - } - } - - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_key -- - * - * Retrieves the key of the current field. The resulting key is valid - * while @iter is valid. - * - * Returns: - * A string that should not be modified or freed. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -const char * -bson_iter_key(const bson_iter_t *iter) /* IN */ -{ - BSON_ASSERT(iter); - - return bson_iter_key_unsafe(iter); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_type -- - * - * Retrieves the type of the current field. It may be useful to check - * the type using the BSON_ITER_HOLDS_*() macros. - * - * Returns: - * A bson_type_t. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bson_type_t -bson_iter_type(const bson_iter_t *iter) /* IN */ -{ - BSON_ASSERT(iter); - BSON_ASSERT(iter->raw); - BSON_ASSERT(iter->len); - - return bson_iter_type_unsafe(iter); -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_iter_next_internal -- - * - * Internal function to advance @iter to the next field and retrieve - * the key and BSON type before error-checking. @next_keylen is - * the key length of the next field being iterated or 0 if this is - * not known. - * - * Return: - * true if an element was decoded, else false. - * - * Side effects: - * @key and @bson_type are set. - * - * If the return value is false: - * - @iter is invalidated: @iter->raw is NULLed - * - @unsupported is set to true if the bson type is unsupported - * - otherwise if the BSON is corrupt, @iter->err_off is nonzero - * - otherwise @bson_type is set to BSON_TYPE_EOD - * - *-------------------------------------------------------------------------- - */ - -static bool -_bson_iter_next_internal(bson_iter_t *iter, /* INOUT */ - uint32_t next_keylen, /* IN */ - const char **key, /* OUT */ - uint32_t *bson_type, /* OUT */ - bool *unsupported) /* OUT */ -{ - const uint8_t *data; - uint32_t o; - unsigned int len; - - BSON_ASSERT(iter); - - *unsupported = false; - - if (!iter->raw) { - *key = NULL; - *bson_type = BSON_TYPE_EOD; - return false; - } - - data = iter->raw; - len = iter->len; - - iter->off = iter->next_off; - iter->type = iter->off; - iter->key = iter->off + 1; - iter->d1 = 0; - iter->d2 = 0; - iter->d3 = 0; - iter->d4 = 0; - - if (next_keylen == 0) { - /* iterate from start to end of NULL-terminated key string */ - for (o = iter->key; o < len; o++) { - if (!data[o]) { - iter->d1 = ++o; - goto fill_data_fields; - } - } - } else { - o = iter->key + next_keylen + 1; - iter->d1 = o; - goto fill_data_fields; - } - - goto mark_invalid; - -fill_data_fields: - - *key = bson_iter_key_unsafe(iter); - *bson_type = ITER_TYPE(iter); - - switch (*bson_type) { - case BSON_TYPE_DATE_TIME: - case BSON_TYPE_DOUBLE: - case BSON_TYPE_INT64: - case BSON_TYPE_TIMESTAMP: - iter->next_off = o + 8; - break; - case BSON_TYPE_CODE: - case BSON_TYPE_SYMBOL: - case BSON_TYPE_UTF8: { - if ((o + 4) >= len) { - iter->err_off = o; - goto mark_invalid; - } - - iter->d2 = o + 4; - const uint32_t l = mlib_read_u32le(iter->raw + iter->d1); - - if (l > (len - (o + 4))) { - iter->err_off = o; - goto mark_invalid; - } - - iter->next_off = o + 4 + l; - - /* - * Make sure the string length includes the NUL byte. - */ - if (BSON_UNLIKELY((l == 0) || (iter->next_off >= len))) { - iter->err_off = o; - goto mark_invalid; - } - - /* - * Make sure the last byte is a NUL byte. - */ - if (BSON_UNLIKELY((iter->raw + iter->d2)[l - 1] != '\0')) { - iter->err_off = o + 4 + l - 1; - goto mark_invalid; - } - } break; - case BSON_TYPE_BINARY: { - bson_subtype_t subtype; - - if (o >= (len - 4)) { - iter->err_off = o; - goto mark_invalid; - } - - iter->d2 = o + 4; - iter->d3 = o + 5; - - const uint32_t l = mlib_read_u32le(iter->raw + iter->d1); - - if (l >= (len - o - 4)) { - iter->err_off = o; - goto mark_invalid; - } - - subtype = *(iter->raw + iter->d2); - - if (subtype == BSON_SUBTYPE_BINARY_DEPRECATED) { - if (l < 4) { - iter->err_off = o; - goto mark_invalid; - } - - /* subtype 2 has a redundant length header in the data */ - const int32_t bin_len = mlib_read_i32le(iter->raw + iter->d3); - if (mlib_cmp(bin_len, !=, l - 4)) { - iter->err_off = iter->d3; - goto mark_invalid; - } - } - - iter->next_off = o + 5 + l; - } break; - case BSON_TYPE_ARRAY: - case BSON_TYPE_DOCUMENT: { - if (o >= (len - 4)) { - iter->err_off = o; - goto mark_invalid; - } - - const uint32_t l = mlib_read_u32le(iter->raw + iter->d1); - if ((l > len) || (l > (len - o))) { - iter->err_off = o; - goto mark_invalid; - } - - iter->next_off = o + l; - } break; - case BSON_TYPE_OID: - iter->next_off = o + 12; - break; - case BSON_TYPE_BOOL: { - char val; - - if (iter->d1 >= len) { - iter->err_off = o; - goto mark_invalid; - } - - memcpy(&val, iter->raw + iter->d1, 1); - if (val != 0x00 && val != 0x01) { - iter->err_off = o; - goto mark_invalid; - } - - iter->next_off = o + 1; - } break; - case BSON_TYPE_REGEX: { - bool eor = false; - bool eoo = false; - - for (; o < len; o++) { - if (!data[o]) { - iter->d2 = ++o; - eor = true; - break; - } - } - - if (!eor) { - iter->err_off = iter->next_off; - goto mark_invalid; - } - - for (; o < len; o++) { - if (!data[o]) { - eoo = true; - break; - } - } - - if (!eoo) { - iter->err_off = iter->next_off; - goto mark_invalid; - } - - iter->next_off = o + 1; - } break; - case BSON_TYPE_DBPOINTER: { - if (o >= (len - 4)) { - iter->err_off = o; - goto mark_invalid; - } - - iter->d2 = o + 4; - const uint32_t l = mlib_read_u32le(iter->raw + iter->d1); - - /* Check valid string length. l counts '\0' but not 4 bytes for itself. */ - if (l == 0 || l > (len - o - 4)) { - iter->err_off = o; - goto mark_invalid; - } - - if (*(iter->raw + o + l + 3)) { - /* not null terminated */ - iter->err_off = o + l + 3; - goto mark_invalid; - } - - iter->d3 = o + 4 + l; - iter->next_off = o + 4 + l + 12; - } break; - case BSON_TYPE_CODEWSCOPE: { - if ((len < 19) || (o >= (len - 14))) { - iter->err_off = o; - goto mark_invalid; - } - - iter->d2 = o + 4; - iter->d3 = o + 8; - - const uint32_t l1 = mlib_read_u32le(iter->raw + iter->d1); - - if ((l1 < 14) || (l1 >= (len - o))) { - iter->err_off = o; - goto mark_invalid; - } - - iter->next_off = o + l1; - - if (iter->next_off >= len) { - iter->err_off = o; - goto mark_invalid; - } - - const uint32_t l2 = mlib_read_u32le(iter->raw + iter->d2); - - if (l2 == 0 || l2 >= (len - o - 4 - 4)) { - iter->err_off = o; - goto mark_invalid; - } - - if ((o + 4 + 4 + l2 + 4) >= iter->next_off) { - iter->err_off = o + 4; - goto mark_invalid; - } - - iter->d4 = o + 4 + 4 + l2; - const uint32_t doclen = mlib_read_u32le(iter->raw + iter->d4); - - if ((o + 4 + 4 + l2 + doclen) != iter->next_off) { - iter->err_off = o + 4 + 4 + l2; - goto mark_invalid; - } - } break; - case BSON_TYPE_INT32: - iter->next_off = o + 4; - break; - case BSON_TYPE_DECIMAL128: - iter->next_off = o + 16; - break; - case BSON_TYPE_MAXKEY: - case BSON_TYPE_MINKEY: - case BSON_TYPE_NULL: - case BSON_TYPE_UNDEFINED: - iter->next_off = o; - break; - default: - *unsupported = true; - /* FALL THROUGH */ - case BSON_TYPE_EOD: - iter->err_off = o; - goto mark_invalid; - } - - /* - * Check to see if any of the field locations would overflow the - * current BSON buffer. If so, set the error location to the offset - * of where the field starts. - */ - if (iter->next_off >= len) { - iter->err_off = o; - goto mark_invalid; - } - - iter->err_off = 0; - - return true; - -mark_invalid: - iter->raw = NULL; - iter->len = 0; - iter->next_off = 0; - - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_next -- - * - * Advances @iter to the next field of the underlying BSON document. - * If all fields have been exhausted, then %false is returned. - * - * It is a programming error to use @iter after this function has - * returned false. - * - * Returns: - * true if the iter was advanced to the next record. - * otherwise false and @iter should be considered invalid. - * - * Side effects: - * @iter may be invalidated. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_iter_next(bson_iter_t *iter) /* INOUT */ -{ - uint32_t bson_type; - const char *key; - bool unsupported; - - return _bson_iter_next_internal(iter, 0, &key, &bson_type, &unsupported); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_binary -- - * - * Retrieves the BSON_TYPE_BINARY field. The subtype is stored in - * @subtype. The length of @binary in bytes is stored in @binary_len. - * - * @binary should not be modified or freed and is only valid while - * @iter's bson_t is valid and unmodified. - * - * Note: Public constraints are tighter than private ones. - * API documentation says it's "a programming error to call this function - * when ``iter`` is not observing an element of type BSON_TYPE_BINARY.". - * Privately only, we do check the iterator type and we output NULL and - * BSON_SUBTYPE_BINARY when the type is incorrect. - * - * Parameters: - * @iter: A bson_iter_t - * @subtype: A location for the binary subtype. - * @binary_len: A location for the length of @binary. - * @binary: A location for a pointer to the binary data. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_iter_binary(const bson_iter_t *iter, /* IN */ - bson_subtype_t *subtype, /* OUT */ - uint32_t *binary_len, /* OUT */ - const uint8_t **binary) /* OUT */ -{ - bson_subtype_t backup; - - BSON_ASSERT(iter); - BSON_ASSERT(!binary || binary_len); - - if (ITER_TYPE(iter) == BSON_TYPE_BINARY) { - if (!subtype) { - subtype = &backup; - } - - *subtype = (bson_subtype_t) * (iter->raw + iter->d2); - - if (binary) { - *binary_len = mlib_read_u32le(iter->raw + iter->d1); - *binary = iter->raw + iter->d3; - - if (*subtype == BSON_SUBTYPE_BINARY_DEPRECATED) { - *binary_len -= sizeof(int32_t); - *binary += sizeof(int32_t); - } - } - - return; - } - - if (binary) { - *binary = NULL; - } - - if (binary_len) { - *binary_len = 0; - } - - if (subtype) { - *subtype = BSON_SUBTYPE_BINARY; - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_overwrite_binary -- - * - * Obtain temporary mutable access to the contents of a BSON_TYPE_BINARY - * field. It may be modified in content only, without changing length - * or subtype, through a temporary pointer that's only valid until the - * underlying bson_t is modified or deleted. - * - * Note: Public constraints are tighter than private ones. - * API documentation says it's "a programming error to call this function - * when ``iter`` is not observing an element of type BSON_TYPE_BINARY.". - * Privately only, we do check the iterator type and we return NULL when - * the type is incorrect. - * - * Parameters: - * @iter: A bson_iter_t - * @binary_len: A location for the length of @binary. - * @binary: A location for a pointer to the binary data. - * - * Returns: - * On success, returns a pointer in *binary and a length in *binary_len. - * The pointer is invalidated when the underlying bson_t is destroyed or modified. - * If the iter does not point to a binary item of the indicated subtype, - * returns NULL in *binary and 0 *binary_len. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -BSON_EXPORT(void) -bson_iter_overwrite_binary(bson_iter_t *iter, /* IN */ - bson_subtype_t subtype, /* IN */ - uint32_t *binary_len, /* OUT */ - uint8_t **binary) /* OUT */ -{ - BSON_ASSERT_PARAM(iter); - BSON_OPTIONAL_PARAM(binary_len); - BSON_OPTIONAL_PARAM(binary); - - bson_subtype_t iter_subtype; - uint32_t iter_binary_len; - const uint8_t *iter_binary; - bson_iter_binary(iter, &iter_subtype, &iter_binary_len, &iter_binary); - - if (iter_binary && iter_subtype == subtype) { - // All of bson_iter_overwrite_* work by casting away const from iter->raw. - if (binary) { - *binary = (void *)iter_binary; - } - if (binary_len) { - *binary_len = iter_binary_len; - } - return; - } - if (binary) { - *binary = NULL; - } - if (binary_len) { - *binary_len = 0; - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_binary_subtype -- - * - * Retrieves the subtype of a BSON_TYPE_BINARY field. - * - * Note: Public constraints are tighter than private ones. - * API documentation says it's "a programming error to call this function - * when ``iter`` is not observing an element of type BSON_TYPE_BINARY.". - * Privately only, we do check the iterator type and return - * BSON_SUBTYPE_BINARY when the type is incorrect. - * - * Parameters: - * @iter: A bson_iter_t - * - * Returns: - * Same as the @subtype OUT parameter from bson_iter_binary(). - * If the iterator is valid, returns the referenced subtype. Otherwise, - * returns BSON_SUBTYPE_BINARY as a fallback. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -BSON_EXPORT(bson_subtype_t) -bson_iter_binary_subtype(const bson_iter_t *iter) -{ - bson_subtype_t result; - bson_iter_binary(iter, &result, NULL, NULL); - return result; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_binary_equal -- - * - * Compare two BSON_TYPE_BINARY fields for equality. - * - * Parameters: - * @iter_a: First bson_iter_t to compare - * @iter_b: Second bson_iter_t to compare - * - * Returns: - * true if both iterators point to BSON_TYPE_BINARY fields with - * identical subtype and contents. false if there is any difference. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -BSON_EXPORT(bool) -bson_iter_binary_equal(const bson_iter_t *iter_a, const bson_iter_t *iter_b) -{ - BSON_ASSERT_PARAM(iter_a); - BSON_ASSERT_PARAM(iter_b); - - if (BSON_ITER_HOLDS_BINARY(iter_a) && BSON_ITER_HOLDS_BINARY(iter_b)) { - bson_subtype_t subtypes[2]; - uint32_t lengths[2]; - const uint8_t *data[2]; - bson_iter_binary(iter_a, &subtypes[0], &lengths[0], &data[0]); - bson_iter_binary(iter_b, &subtypes[1], &lengths[1], &data[1]); - return subtypes[0] == subtypes[1] && lengths[0] == lengths[1] && 0 == memcmp(data[0], data[1], lengths[0]); - } else { - return false; - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_bool -- - * - * Retrieves the current field of type BSON_TYPE_BOOL. - * - * Returns: - * true or false, dependent on bson document. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_iter_bool(const bson_iter_t *iter) /* IN */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_BOOL) { - return bson_iter_bool_unsafe(iter); - } - - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_as_bool -- - * - * If @iter is on a boolean field, returns the boolean. If it is on a - * non-boolean field such as int32, int64, or double, it will convert - * the value to a boolean. - * - * Zero is false, and non-zero is true. - * - * Returns: - * true or false, dependent on field type. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_iter_as_bool(const bson_iter_t *iter) /* IN */ -{ - BSON_ASSERT(iter); - - switch ((int)ITER_TYPE(iter)) { - case BSON_TYPE_BOOL: - return bson_iter_bool(iter); - case BSON_TYPE_DOUBLE: - return !(bson_iter_double(iter) == 0.0); - case BSON_TYPE_INT64: - return !(bson_iter_int64(iter) == 0); - case BSON_TYPE_INT32: - return !(bson_iter_int32(iter) == 0); - case BSON_TYPE_UTF8: - return true; - case BSON_TYPE_NULL: - case BSON_TYPE_UNDEFINED: - return false; - default: - return true; - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_double -- - * - * Retrieves the current field of type BSON_TYPE_DOUBLE. - * - * Returns: - * A double. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -double -bson_iter_double(const bson_iter_t *iter) /* IN */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_DOUBLE) { - return bson_iter_double_unsafe(iter); - } - - return 0; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_as_double -- - * - * If @iter is on a field of type BSON_TYPE_DOUBLE, - * returns the double. If it is on an integer field - * such as int32, int64, or bool, it will convert - * the value to a double. - * - * - * Returns: - * A double. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -double -bson_iter_as_double(const bson_iter_t *iter) /* IN */ -{ - BSON_ASSERT(iter); - - switch ((int)ITER_TYPE(iter)) { - case BSON_TYPE_BOOL: - return (double)bson_iter_bool(iter); - case BSON_TYPE_DOUBLE: - return bson_iter_double(iter); - case BSON_TYPE_INT32: - return (double)bson_iter_int32(iter); - case BSON_TYPE_INT64: - return (double)bson_iter_int64(iter); - default: - return 0; - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_int32 -- - * - * Retrieves the value of the field of type BSON_TYPE_INT32. - * - * Returns: - * A 32-bit signed integer. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -int32_t -bson_iter_int32(const bson_iter_t *iter) /* IN */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_INT32) { - return bson_iter_int32_unsafe(iter); - } - - return 0; -} - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_int64 -- - * - * Retrieves a 64-bit signed integer for the current BSON_TYPE_INT64 - * field. - * - * Returns: - * A 64-bit signed integer. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -int64_t -bson_iter_int64(const bson_iter_t *iter) /* IN */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_INT64) { - return bson_iter_int64_unsafe(iter); - } - - return 0; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_as_int64 -- - * - * If @iter is not an int64 field, it will try to convert the value to - * an int64. Such field types include: - * - * - bool - * - double - * - int32 - * - * Returns: - * An int64_t. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -int64_t -bson_iter_as_int64(const bson_iter_t *iter) /* IN */ -{ - BSON_ASSERT(iter); - - switch ((int)ITER_TYPE(iter)) { - case BSON_TYPE_BOOL: - return (int64_t)bson_iter_bool(iter); - case BSON_TYPE_DOUBLE: - return (int64_t)bson_iter_double(iter); - case BSON_TYPE_INT64: - return bson_iter_int64(iter); - case BSON_TYPE_INT32: - return (int64_t)bson_iter_int32(iter); - default: - return 0; - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_decimal128 -- - * - * This function retrieves the current field of type - *%BSON_TYPE_DECIMAL128. - * The result is valid while @iter is valid, and is stored in @dec. - * - * Returns: - * - * True on success, false on failure. - * - * Side Effects: - * None. - * - *-------------------------------------------------------------------------- - */ -bool -bson_iter_decimal128(const bson_iter_t *iter, /* IN */ - bson_decimal128_t *dec) /* OUT */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_DECIMAL128) { - bson_iter_decimal128_unsafe(iter, dec); - return true; - } - - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_oid -- - * - * Retrieves the current field of type %BSON_TYPE_OID. The result is - * valid while @iter is valid. - * - * Returns: - * A bson_oid_t that should not be modified or freed. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -const bson_oid_t * -bson_iter_oid(const bson_iter_t *iter) /* IN */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_OID) { - return bson_iter_oid_unsafe(iter); - } - - return NULL; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_regex -- - * - * Fetches the current field from the iter which should be of type - * BSON_TYPE_REGEX. - * - * Returns: - * Regex from @iter. This should not be modified or freed. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -const char * -bson_iter_regex(const bson_iter_t *iter, /* IN */ - const char **options) /* IN */ -{ - const char *ret = NULL; - const char *ret_options = NULL; - - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_REGEX) { - ret = (const char *)(iter->raw + iter->d1); - ret_options = (const char *)(iter->raw + iter->d2); - } - - if (options) { - *options = ret_options; - } - - return ret; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_utf8 -- - * - * Retrieves the current field of type %BSON_TYPE_UTF8 as a UTF-8 - * encoded string. - * - * Parameters: - * @iter: A bson_iter_t. - * @length: A location for the length of the string. - * - * Returns: - * A string that should not be modified or freed. - * - * Side effects: - * @length will be set to the result strings length if non-NULL. - * - *-------------------------------------------------------------------------- - */ - -const char * -bson_iter_utf8(const bson_iter_t *iter, /* IN */ - uint32_t *length) /* OUT */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_UTF8) { - if (length) { - *length = bson_iter_utf8_len_unsafe(iter); - } - - return (const char *)(iter->raw + iter->d2); - } - - if (length) { - *length = 0; - } - - return NULL; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_dup_utf8 -- - * - * Copies the current UTF-8 element into a newly allocated string. The - * string should be freed using bson_free() when the caller is - * finished with it. - * - * Returns: - * A newly allocated char* that should be freed with bson_free(). - * - * Side effects: - * @length will be set to the result strings length if non-NULL. - * - *-------------------------------------------------------------------------- - */ - -char * -bson_iter_dup_utf8(const bson_iter_t *iter, /* IN */ - uint32_t *length) /* OUT */ -{ - uint32_t local_length = 0; - const char *str; - char *ret = NULL; - - BSON_ASSERT(iter); - - if ((str = bson_iter_utf8(iter, &local_length))) { - ret = bson_malloc0(local_length + 1); - memcpy(ret, str, local_length); - ret[local_length] = '\0'; - } - - if (length) { - *length = local_length; - } - - return ret; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_code -- - * - * Retrieves the current field of type %BSON_TYPE_CODE. The length of - * the resulting string is stored in @length. - * - * Parameters: - * @iter: A bson_iter_t. - * @length: A location for the code length. - * - * Returns: - * A NUL-terminated string containing the code which should not be - * modified or freed. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -const char * -bson_iter_code(const bson_iter_t *iter, /* IN */ - uint32_t *length) /* OUT */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_CODE) { - if (length) { - *length = bson_iter_utf8_len_unsafe(iter); - } - - return (const char *)(iter->raw + iter->d2); - } - - if (length) { - *length = 0; - } - - return NULL; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_codewscope -- - * - * Similar to bson_iter_code() but with a scope associated encoded as - * a BSON document. @scope should not be modified or freed. It is - * valid while @iter is valid. - * - * Parameters: - * @iter: A #bson_iter_t. - * @length: A location for the length of resulting string. - * @scope_len: A location for the length of @scope. - * @scope: A location for the scope encoded as BSON. - * - * Returns: - * A NUL-terminated string that should not be modified or freed. - * - * Side effects: - * @length is set to the resulting string length in bytes. - * @scope_len is set to the length of @scope in bytes. - * @scope is set to the scope documents buffer which can be - * turned into a bson document with bson_init_static(). - * - *-------------------------------------------------------------------------- - */ - -const char * -bson_iter_codewscope(const bson_iter_t *iter, /* IN */ - uint32_t *length, /* OUT */ - uint32_t *scope_len, /* OUT */ - const uint8_t **scope) /* OUT */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_CODEWSCOPE) { - if (length) { - const uint32_t len = mlib_read_u32le(iter->raw + iter->d2); - /* The string length was checked > 0 in _bson_iter_next_internal. */ - BSON_ASSERT(len > 0); - *length = len - 1; - } - - *scope_len = mlib_read_u32le(iter->raw + iter->d4); - *scope = iter->raw + iter->d4; - return (const char *)(iter->raw + iter->d3); - } - - if (length) { - *length = 0; - } - - if (scope_len) { - *scope_len = 0; - } - - if (scope) { - *scope = NULL; - } - - return NULL; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_dbpointer -- - * - * Retrieves a BSON_TYPE_DBPOINTER field. @collection_len will be set - * to the length of the collection name. The collection name will be - * placed into @collection. The oid will be placed into @oid. - * - * @collection and @oid should not be modified. - * - * Parameters: - * @iter: A #bson_iter_t. - * @collection_len: A location for the length of @collection. - * @collection: A location for the collection name. - * @oid: A location for the oid. - * - * Returns: - * None. - * - * Side effects: - * @collection_len is set to the length of @collection in bytes - * excluding the null byte. - * @collection is set to the collection name, including a terminating - * null byte. - * @oid is initialized with the oid. - * - *-------------------------------------------------------------------------- - */ - -void -bson_iter_dbpointer(const bson_iter_t *iter, /* IN */ - uint32_t *collection_len, /* OUT */ - const char **collection, /* OUT */ - const bson_oid_t **oid) /* OUT */ -{ - BSON_ASSERT(iter); - - if (collection) { - *collection = NULL; - } - - if (oid) { - *oid = NULL; - } - - if (ITER_TYPE(iter) == BSON_TYPE_DBPOINTER) { - if (collection_len) { - *collection_len = mlib_read_u32le(iter->raw + iter->d1); - - if ((*collection_len) > 0) { - (*collection_len)--; - } - } - - if (collection) { - *collection = (const char *)(iter->raw + iter->d2); - } - - if (oid) { - *oid = (const bson_oid_t *)(iter->raw + iter->d3); - } - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_symbol -- - * - * Retrieves the symbol of the current field of type BSON_TYPE_SYMBOL. - * - * Parameters: - * @iter: A bson_iter_t. - * @length: A location for the length of the symbol. - * - * Returns: - * A string containing the symbol as UTF-8. The value should not be - * modified or freed. - * - * Side effects: - * @length is set to the resulting strings length in bytes, - * excluding the null byte. - * - *-------------------------------------------------------------------------- - */ - -const char * -bson_iter_symbol(const bson_iter_t *iter, /* IN */ - uint32_t *length) /* OUT */ -{ - const char *ret = NULL; - uint32_t ret_length = 0; - - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_SYMBOL) { - ret = (const char *)(iter->raw + iter->d2); - ret_length = bson_iter_utf8_len_unsafe(iter); - } - - if (length) { - *length = ret_length; - } - - return ret; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_date_time -- - * - * Fetches the number of milliseconds elapsed since the UNIX epoch. - * This value can be negative as times before 1970 are valid. - * - * Returns: - * A signed 64-bit integer containing the number of milliseconds. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -int64_t -bson_iter_date_time(const bson_iter_t *iter) /* IN */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_DATE_TIME) { - return bson_iter_int64_unsafe(iter); - } - - return 0; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_time_t -- - * - * Retrieves the current field of type BSON_TYPE_DATE_TIME as a - * time_t. - * - * Returns: - * A #time_t of the number of seconds since UNIX epoch in UTC. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -time_t -bson_iter_time_t(const bson_iter_t *iter) /* IN */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_DATE_TIME) { - return bson_iter_time_t_unsafe(iter); - } - - return 0; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_timestamp -- - * - * Fetches the current field if it is a BSON_TYPE_TIMESTAMP. - * - * Parameters: - * @iter: A #bson_iter_t. - * @timestamp: a location for the timestamp. - * @increment: A location for the increment. - * - * Returns: - * None. - * - * Side effects: - * @timestamp is initialized. - * @increment is initialized. - * - *-------------------------------------------------------------------------- - */ - -void -bson_iter_timestamp(const bson_iter_t *iter, /* IN */ - uint32_t *timestamp, /* OUT */ - uint32_t *increment) /* OUT */ -{ - uint32_t ret_timestamp = 0; - uint32_t ret_increment = 0; - - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_TIMESTAMP) { - ret_increment = mlib_read_u32le(iter->raw + iter->d1); - ret_timestamp = mlib_read_u32le(iter->raw + iter->d1 + sizeof(uint32_t)); - } - - if (timestamp) { - *timestamp = ret_timestamp; - } - - if (increment) { - *increment = ret_increment; - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_timeval -- - * - * Retrieves the current field of type BSON_TYPE_DATE_TIME and stores - * it into the struct timeval provided. tv->tv_sec is set to the - * number of seconds since the UNIX epoch in UTC. - * - * Since BSON_TYPE_DATE_TIME does not support fractions of a second, - * tv->tv_usec will always be set to zero. - * - * Returns: - * None. - * - * Side effects: - * @tv is initialized. - * - *-------------------------------------------------------------------------- - */ - -void -bson_iter_timeval(const bson_iter_t *iter, /* IN */ - struct timeval *tv) /* OUT */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_DATE_TIME) { - bson_iter_timeval_unsafe(iter, tv); - return; - } - - memset(tv, 0, sizeof *tv); -} - - -/** - * bson_iter_document: - * @iter: a bson_iter_t. - * @document_len: A location for the document length. - * @document: A location for a pointer to the document buffer. - * - */ -/* - *-------------------------------------------------------------------------- - * - * bson_iter_document -- - * - * Retrieves the data to the document BSON structure and stores the - * length of the document buffer in @document_len and the document - * buffer in @document. - * - * If you would like to iterate over the child contents, you might - * consider creating a bson_t on the stack such as the following. It - * allows you to call functions taking a const bson_t* only. - * - * bson_t b; - * uint32_t len; - * const uint8_t *data; - * - * bson_iter_document(iter, &len, &data); - * - * if (bson_init_static (&b, data, len)) { - * ... - * } - * - * There is no need to cleanup the bson_t structure as no data can be - * modified in the process of its use (as it is static/const). - * - * Returns: - * None. - * - * Side effects: - * @document_len is initialized. - * @document is initialized. - * - *-------------------------------------------------------------------------- - */ - -void -bson_iter_document(const bson_iter_t *iter, /* IN */ - uint32_t *document_len, /* OUT */ - const uint8_t **document) /* OUT */ -{ - BSON_ASSERT(iter); - BSON_ASSERT(document_len); - BSON_ASSERT(document); - - *document = NULL; - *document_len = 0; - - if (ITER_TYPE(iter) == BSON_TYPE_DOCUMENT) { - *document_len = mlib_read_u32le(iter->raw + iter->d1); - *document = (iter->raw + iter->d1); - } -} - - -/** - * bson_iter_array: - * @iter: a #bson_iter_t. - * @array_len: A location for the array length. - * @array: A location for a pointer to the array buffer. - */ -/* - *-------------------------------------------------------------------------- - * - * bson_iter_array -- - * - * Retrieves the data to the array BSON structure and stores the - * length of the array buffer in @array_len and the array buffer in - * @array. - * - * If you would like to iterate over the child contents, you might - * consider creating a bson_t on the stack such as the following. It - * allows you to call functions taking a const bson_t* only. - * - * bson_t b; - * uint32_t len; - * const uint8_t *data; - * - * bson_iter_array (iter, &len, &data); - * - * if (bson_init_static (&b, data, len)) { - * ... - * } - * - * There is no need to cleanup the #bson_t structure as no data can be - * modified in the process of its use. - * - * Returns: - * None. - * - * Side effects: - * @array_len is initialized. - * @array is initialized. - * - *-------------------------------------------------------------------------- - */ - -void -bson_iter_array(const bson_iter_t *iter, /* IN */ - uint32_t *array_len, /* OUT */ - const uint8_t **array) /* OUT */ -{ - BSON_ASSERT(iter); - BSON_ASSERT(array_len); - BSON_ASSERT(array); - - *array = NULL; - *array_len = 0; - - if (ITER_TYPE(iter) == BSON_TYPE_ARRAY) { - *array_len = mlib_read_u32le(iter->raw + iter->d1); - *array = (iter->raw + iter->d1); - } -} - - -#define VISIT_FIELD(name) visitor->visit_##name && visitor->visit_##name -#define VISIT_AFTER VISIT_FIELD(after) -#define VISIT_BEFORE VISIT_FIELD(before) -#define VISIT_CORRUPT \ - if (visitor->visit_corrupt) \ - visitor->visit_corrupt -#define VISIT_DOUBLE VISIT_FIELD(double) -#define VISIT_UTF8 VISIT_FIELD(utf8) -#define VISIT_DOCUMENT VISIT_FIELD(document) -#define VISIT_ARRAY VISIT_FIELD(array) -#define VISIT_BINARY VISIT_FIELD(binary) -#define VISIT_UNDEFINED VISIT_FIELD(undefined) -#define VISIT_OID VISIT_FIELD(oid) -#define VISIT_BOOL VISIT_FIELD(bool) -#define VISIT_DATE_TIME VISIT_FIELD(date_time) -#define VISIT_NULL VISIT_FIELD(null) -#define VISIT_REGEX VISIT_FIELD(regex) -#define VISIT_DBPOINTER VISIT_FIELD(dbpointer) -#define VISIT_CODE VISIT_FIELD(code) -#define VISIT_SYMBOL VISIT_FIELD(symbol) -#define VISIT_CODEWSCOPE VISIT_FIELD(codewscope) -#define VISIT_INT32 VISIT_FIELD(int32) -#define VISIT_TIMESTAMP VISIT_FIELD(timestamp) -#define VISIT_INT64 VISIT_FIELD(int64) -#define VISIT_DECIMAL128 VISIT_FIELD(decimal128) -#define VISIT_MAXKEY VISIT_FIELD(maxkey) -#define VISIT_MINKEY VISIT_FIELD(minkey) - - -bool -bson_iter_visit_all(bson_iter_t *iter, /* INOUT */ - const bson_visitor_t *visitor, /* IN */ - void *data) /* IN */ -{ - uint32_t bson_type = 0; - const char *key = NULL; - bool unsupported; - - BSON_ASSERT(iter); - BSON_ASSERT(visitor); - - while (_bson_iter_next_internal(iter, 0, &key, &bson_type, &unsupported)) { - if (*key && !bson_utf8_validate(key, strlen(key), false)) { - iter->err_off = iter->off; - break; - } - - if (VISIT_BEFORE(iter, key, data)) { - return true; - } - - switch (bson_type) { - case BSON_TYPE_DOUBLE: - - if (VISIT_DOUBLE(iter, key, bson_iter_double(iter), data)) { - return true; - } - - break; - case BSON_TYPE_UTF8: { - uint32_t utf8_len; - const char *utf8; - - utf8 = bson_iter_utf8(iter, &utf8_len); - - if (!bson_utf8_validate(utf8, utf8_len, true)) { - iter->err_off = iter->off; - return true; - } - - if (VISIT_UTF8(iter, key, utf8_len, utf8, data)) { - return true; - } - } break; - case BSON_TYPE_DOCUMENT: { - const uint8_t *docbuf = NULL; - uint32_t doclen = 0; - bson_t b; - - bson_iter_document(iter, &doclen, &docbuf); - - if (!bson_init_static(&b, docbuf, doclen)) { - iter->err_off = iter->off; - break; - } - if (VISIT_DOCUMENT(iter, key, &b, data)) { - return true; - } - } break; - case BSON_TYPE_ARRAY: { - const uint8_t *docbuf = NULL; - uint32_t doclen = 0; - bson_t b; - - bson_iter_array(iter, &doclen, &docbuf); - - if (!bson_init_static(&b, docbuf, doclen)) { - iter->err_off = iter->off; - break; - } - if (VISIT_ARRAY(iter, key, &b, data)) { - return true; - } - } break; - case BSON_TYPE_BINARY: { - const uint8_t *binary = NULL; - bson_subtype_t subtype = BSON_SUBTYPE_BINARY; - uint32_t binary_len = 0; - - bson_iter_binary(iter, &subtype, &binary_len, &binary); - - if (VISIT_BINARY(iter, key, subtype, binary_len, binary, data)) { - return true; - } - } break; - case BSON_TYPE_UNDEFINED: - - if (VISIT_UNDEFINED(iter, key, data)) { - return true; - } - - break; - case BSON_TYPE_OID: - - if (VISIT_OID(iter, key, bson_iter_oid(iter), data)) { - return true; - } - - break; - case BSON_TYPE_BOOL: - - if (VISIT_BOOL(iter, key, bson_iter_bool(iter), data)) { - return true; - } - - break; - case BSON_TYPE_DATE_TIME: - - if (VISIT_DATE_TIME(iter, key, bson_iter_date_time(iter), data)) { - return true; - } - - break; - case BSON_TYPE_NULL: - - if (VISIT_NULL(iter, key, data)) { - return true; - } - - break; - case BSON_TYPE_REGEX: { - const char *regex = NULL; - const char *options = NULL; - regex = bson_iter_regex(iter, &options); - - if (!bson_utf8_validate(regex, strlen(regex), true)) { - iter->err_off = iter->off; - return true; - } - - if (VISIT_REGEX(iter, key, regex, options, data)) { - return true; - } - } break; - case BSON_TYPE_DBPOINTER: { - uint32_t collection_len = 0; - const char *collection = NULL; - const bson_oid_t *oid = NULL; - - bson_iter_dbpointer(iter, &collection_len, &collection, &oid); - - if (!bson_utf8_validate(collection, collection_len, true)) { - iter->err_off = iter->off; - return true; - } - - if (VISIT_DBPOINTER(iter, key, collection_len, collection, oid, data)) { - return true; - } - } break; - case BSON_TYPE_CODE: { - uint32_t code_len; - const char *code; - - code = bson_iter_code(iter, &code_len); - - if (!bson_utf8_validate(code, code_len, true)) { - iter->err_off = iter->off; - return true; - } - - if (VISIT_CODE(iter, key, code_len, code, data)) { - return true; - } - } break; - case BSON_TYPE_SYMBOL: { - uint32_t symbol_len; - const char *symbol; - - symbol = bson_iter_symbol(iter, &symbol_len); - - if (!bson_utf8_validate(symbol, symbol_len, true)) { - iter->err_off = iter->off; - return true; - } - - if (VISIT_SYMBOL(iter, key, symbol_len, symbol, data)) { - return true; - } - } break; - case BSON_TYPE_CODEWSCOPE: { - uint32_t length = 0; - const char *code; - const uint8_t *docbuf = NULL; - uint32_t doclen = 0; - bson_t b; - - code = bson_iter_codewscope(iter, &length, &doclen, &docbuf); - - if (!bson_utf8_validate(code, length, true)) { - iter->err_off = iter->off; - return true; - } - - if (!bson_init_static(&b, docbuf, doclen)) { - iter->err_off = iter->off; - break; - } - if (VISIT_CODEWSCOPE(iter, key, length, code, &b, data)) { - return true; - } - } break; - case BSON_TYPE_INT32: - - if (VISIT_INT32(iter, key, bson_iter_int32(iter), data)) { - return true; - } - - break; - case BSON_TYPE_TIMESTAMP: { - uint32_t timestamp; - uint32_t increment; - bson_iter_timestamp(iter, ×tamp, &increment); - - if (VISIT_TIMESTAMP(iter, key, timestamp, increment, data)) { - return true; - } - } break; - case BSON_TYPE_INT64: - - if (VISIT_INT64(iter, key, bson_iter_int64(iter), data)) { - return true; - } - - break; - case BSON_TYPE_DECIMAL128: { - bson_decimal128_t dec; - bson_iter_decimal128(iter, &dec); - - if (VISIT_DECIMAL128(iter, key, &dec, data)) { - return true; - } - } break; - case BSON_TYPE_MAXKEY: - - if (VISIT_MAXKEY(iter, bson_iter_key_unsafe(iter), data)) { - return true; - } - - break; - case BSON_TYPE_MINKEY: - - if (VISIT_MINKEY(iter, bson_iter_key_unsafe(iter), data)) { - return true; - } - - break; - case BSON_TYPE_EOD: - default: - break; - } - - if (VISIT_AFTER(iter, bson_iter_key_unsafe(iter), data)) { - return true; - } - } - - if (iter->err_off) { - if (unsupported && visitor->visit_unsupported_type && bson_utf8_validate(key, strlen(key), false)) { - visitor->visit_unsupported_type(iter, key, bson_type, data); - return false; - } - - VISIT_CORRUPT(iter, data); - } - -#undef VISIT_FIELD - - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_overwrite_bool -- - * - * Overwrites the current BSON_TYPE_BOOLEAN field with a new value. - * This is performed in-place and therefore no keys are moved. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_iter_overwrite_bool(bson_iter_t *iter, /* IN */ - bool value) /* IN */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_BOOL) { - memcpy((void *)(iter->raw + iter->d1), &value, 1); - } -} - - -void -bson_iter_overwrite_oid(bson_iter_t *iter, const bson_oid_t *value) -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_OID) { - memcpy((void *)(iter->raw + iter->d1), value->bytes, sizeof(value->bytes)); - } -} - - -void -bson_iter_overwrite_timestamp(bson_iter_t *iter, uint32_t timestamp, uint32_t increment) -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_TIMESTAMP) { - void *out = mlib_write_u32le((char *)iter->raw + iter->d1, increment); - mlib_write_u32le(out, timestamp); - } -} - - -void -bson_iter_overwrite_date_time(bson_iter_t *iter, int64_t value) -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_DATE_TIME) { - mlib_write_i64le((char *)iter->raw + iter->d1, value); - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_overwrite_int32 -- - * - * Overwrites the current BSON_TYPE_INT32 field with a new value. - * This is performed in-place and therefore no keys are moved. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_iter_overwrite_int32(bson_iter_t *iter, /* IN */ - int32_t value) /* IN */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_INT32) { - mlib_write_i32le((char *)iter->raw + iter->d1, value); - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_overwrite_int64 -- - * - * Overwrites the current BSON_TYPE_INT64 field with a new value. - * This is performed in-place and therefore no keys are moved. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_iter_overwrite_int64(bson_iter_t *iter, /* IN */ - int64_t value) /* IN */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_INT64) { - mlib_write_i64le((char *)iter->raw + iter->d1, value); - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_overwrite_double -- - * - * Overwrites the current BSON_TYPE_DOUBLE field with a new value. - * This is performed in-place and therefore no keys are moved. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_iter_overwrite_double(bson_iter_t *iter, /* IN */ - double value) /* IN */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_DOUBLE) { - mlib_write_f64le((char *)iter->raw + iter->d1, value); - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_overwrite_decimal128 -- - * - * Overwrites the current BSON_TYPE_DECIMAL128 field with a new value. - * This is performed in-place and therefore no keys are moved. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ -void -bson_iter_overwrite_decimal128(bson_iter_t *iter, /* IN */ - const bson_decimal128_t *value) /* IN */ -{ - BSON_ASSERT(iter); - - if (ITER_TYPE(iter) == BSON_TYPE_DECIMAL128) { - // low bits - void *out = mlib_write_u64le((char *)iter->raw + iter->d1, value->low); - // Followed by high bits - mlib_write_u64le(out, value->high); - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_iter_value -- - * - * Retrieves a bson_value_t containing the boxed value of the current - * element. The result of this function valid until the state of - * iter has been changed (through the use of bson_iter_next()). - * - * Returns: - * A bson_value_t that should not be modified or freed. If you need - * to hold on to the value, use bson_value_copy(). - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -const bson_value_t * -bson_iter_value(bson_iter_t *iter) /* IN */ -{ - bson_value_t *value; - - BSON_ASSERT(iter); - - value = &iter->value; - value->value_type = ITER_TYPE(iter); - - switch (value->value_type) { - case BSON_TYPE_DOUBLE: - value->value.v_double = bson_iter_double(iter); - break; - case BSON_TYPE_UTF8: - value->value.v_utf8.str = (char *)bson_iter_utf8(iter, &value->value.v_utf8.len); - break; - case BSON_TYPE_DOCUMENT: - bson_iter_document(iter, &value->value.v_doc.data_len, (const uint8_t **)&value->value.v_doc.data); - break; - case BSON_TYPE_ARRAY: - bson_iter_array(iter, &value->value.v_doc.data_len, (const uint8_t **)&value->value.v_doc.data); - break; - case BSON_TYPE_BINARY: - bson_iter_binary(iter, - &value->value.v_binary.subtype, - &value->value.v_binary.data_len, - (const uint8_t **)&value->value.v_binary.data); - break; - case BSON_TYPE_OID: - bson_oid_copy(bson_iter_oid(iter), &value->value.v_oid); - break; - case BSON_TYPE_BOOL: - value->value.v_bool = bson_iter_bool(iter); - break; - case BSON_TYPE_DATE_TIME: - value->value.v_datetime = bson_iter_date_time(iter); - break; - case BSON_TYPE_REGEX: - value->value.v_regex.regex = (char *)bson_iter_regex(iter, (const char **)&value->value.v_regex.options); - break; - case BSON_TYPE_DBPOINTER: { - const bson_oid_t *oid; - - bson_iter_dbpointer( - iter, &value->value.v_dbpointer.collection_len, (const char **)&value->value.v_dbpointer.collection, &oid); - bson_oid_copy(oid, &value->value.v_dbpointer.oid); - break; - } - case BSON_TYPE_CODE: - value->value.v_code.code = (char *)bson_iter_code(iter, &value->value.v_code.code_len); - break; - case BSON_TYPE_SYMBOL: - value->value.v_symbol.symbol = (char *)bson_iter_symbol(iter, &value->value.v_symbol.len); - break; - case BSON_TYPE_CODEWSCOPE: - value->value.v_codewscope.code = - (char *)bson_iter_codewscope(iter, - &value->value.v_codewscope.code_len, - &value->value.v_codewscope.scope_len, - (const uint8_t **)&value->value.v_codewscope.scope_data); - break; - case BSON_TYPE_INT32: - value->value.v_int32 = bson_iter_int32(iter); - break; - case BSON_TYPE_TIMESTAMP: - bson_iter_timestamp(iter, &value->value.v_timestamp.timestamp, &value->value.v_timestamp.increment); - break; - case BSON_TYPE_INT64: - value->value.v_int64 = bson_iter_int64(iter); - break; - case BSON_TYPE_DECIMAL128: - bson_iter_decimal128(iter, &(value->value.v_decimal128)); - break; - case BSON_TYPE_NULL: - case BSON_TYPE_UNDEFINED: - case BSON_TYPE_MAXKEY: - case BSON_TYPE_MINKEY: - break; - case BSON_TYPE_EOD: - default: - return NULL; - } - - return value; -} - -uint32_t -bson_iter_key_len(const bson_iter_t *iter) -{ - /* - * f i e l d n a m e \0 _ - * ^ ^ - * | | - * iter->key iter->d1 - * - */ - BSON_ASSERT(iter->d1 > iter->key); - return iter->d1 - iter->key - 1; -} - -bool -bson_iter_init_from_data_at_offset( - bson_iter_t *iter, const uint8_t *data, size_t length, uint32_t offset, uint32_t keylen) -{ - const char *key; - uint32_t bson_type; - bool unsupported; - - BSON_ASSERT(iter); - BSON_ASSERT(data); - - if (BSON_UNLIKELY((length < 5) || (length > INT_MAX))) { - memset(iter, 0, sizeof *iter); - return false; - } - - iter->raw = (uint8_t *)data; - iter->len = (uint32_t)length; - iter->off = 0; - iter->type = 0; - iter->key = 0; - iter->next_off = offset; - iter->err_off = 0; - - if (!_bson_iter_next_internal(iter, keylen, &key, &bson_type, &unsupported)) { - memset(iter, 0, sizeof *iter); - return false; - } - - return true; -} - -uint32_t -bson_iter_offset(bson_iter_t *iter) -{ - return iter->off; -} diff --git a/bsonjs/bson/bson-iter.h b/bsonjs/bson/bson-iter.h deleted file mode 100644 index 1f7e255..0000000 --- a/bsonjs/bson/bson-iter.h +++ /dev/null @@ -1,554 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_ITER_H -#define BSON_ITER_H - - -#include -#include -#include -#include - - -BSON_BEGIN_DECLS - - -#define BSON_ITER_HOLDS_DOUBLE(iter) (bson_iter_type((iter)) == BSON_TYPE_DOUBLE) - -#define BSON_ITER_HOLDS_UTF8(iter) (bson_iter_type((iter)) == BSON_TYPE_UTF8) - -#define BSON_ITER_HOLDS_DOCUMENT(iter) (bson_iter_type((iter)) == BSON_TYPE_DOCUMENT) - -#define BSON_ITER_HOLDS_ARRAY(iter) (bson_iter_type((iter)) == BSON_TYPE_ARRAY) - -#define BSON_ITER_HOLDS_BINARY(iter) (bson_iter_type((iter)) == BSON_TYPE_BINARY) - -#define BSON_ITER_HOLDS_VECTOR(iter) \ - (BSON_ITER_HOLDS_BINARY(iter) && bson_iter_binary_subtype(iter) == BSON_SUBTYPE_VECTOR) - -#define BSON_ITER_HOLDS_VECTOR_INT8(iter) (bson_vector_int8_const_view_from_iter(NULL, iter)) - -#define BSON_ITER_HOLDS_VECTOR_FLOAT32(iter) (bson_vector_float32_const_view_from_iter(NULL, iter)) - -#define BSON_ITER_HOLDS_VECTOR_PACKED_BIT(iter) (bson_vector_packed_bit_const_view_from_iter(NULL, iter)) - -#define BSON_ITER_HOLDS_UNDEFINED(iter) (bson_iter_type((iter)) == BSON_TYPE_UNDEFINED) - -#define BSON_ITER_HOLDS_OID(iter) (bson_iter_type((iter)) == BSON_TYPE_OID) - -#define BSON_ITER_HOLDS_BOOL(iter) (bson_iter_type((iter)) == BSON_TYPE_BOOL) - -#define BSON_ITER_HOLDS_DATE_TIME(iter) (bson_iter_type((iter)) == BSON_TYPE_DATE_TIME) - -#define BSON_ITER_HOLDS_NULL(iter) (bson_iter_type((iter)) == BSON_TYPE_NULL) - -#define BSON_ITER_HOLDS_REGEX(iter) (bson_iter_type((iter)) == BSON_TYPE_REGEX) - -#define BSON_ITER_HOLDS_DBPOINTER(iter) (bson_iter_type((iter)) == BSON_TYPE_DBPOINTER) - -#define BSON_ITER_HOLDS_CODE(iter) (bson_iter_type((iter)) == BSON_TYPE_CODE) - -#define BSON_ITER_HOLDS_SYMBOL(iter) (bson_iter_type((iter)) == BSON_TYPE_SYMBOL) - -#define BSON_ITER_HOLDS_CODEWSCOPE(iter) (bson_iter_type((iter)) == BSON_TYPE_CODEWSCOPE) - -#define BSON_ITER_HOLDS_INT32(iter) (bson_iter_type((iter)) == BSON_TYPE_INT32) - -#define BSON_ITER_HOLDS_TIMESTAMP(iter) (bson_iter_type((iter)) == BSON_TYPE_TIMESTAMP) - -#define BSON_ITER_HOLDS_INT64(iter) (bson_iter_type((iter)) == BSON_TYPE_INT64) - -#define BSON_ITER_HOLDS_DECIMAL128(iter) (bson_iter_type((iter)) == BSON_TYPE_DECIMAL128) - -#define BSON_ITER_HOLDS_MAXKEY(iter) (bson_iter_type((iter)) == BSON_TYPE_MAXKEY) - -#define BSON_ITER_HOLDS_MINKEY(iter) (bson_iter_type((iter)) == BSON_TYPE_MINKEY) - -#define BSON_ITER_HOLDS_INT(iter) (BSON_ITER_HOLDS_INT32(iter) || BSON_ITER_HOLDS_INT64(iter)) - -#define BSON_ITER_HOLDS_NUMBER(iter) (BSON_ITER_HOLDS_INT(iter) || BSON_ITER_HOLDS_DOUBLE(iter)) - -#define BSON_ITER_IS_KEY(iter, key) (0 == strcmp((key), bson_iter_key((iter)))) - - -BSON_EXPORT(const bson_value_t *) -bson_iter_value(bson_iter_t *iter); - - -/** - * bson_iter_utf8_len_unsafe: - * @iter: a bson_iter_t. - * - * Returns the length of a string currently pointed to by @iter. This performs - * no validation so the is responsible for knowing the BSON is valid. Calling - * bson_validate() is one way to do this ahead of time. - */ -static BSON_INLINE uint32_t -bson_iter_utf8_len_unsafe(const bson_iter_t *iter) -{ - uint32_t raw; - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - memcpy(&raw, iter->raw + iter->d1, sizeof(raw)); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - - 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); -} - - -BSON_EXPORT(void) -bson_iter_array(const bson_iter_t *iter, uint32_t *array_len, const uint8_t **array); - - -BSON_EXPORT(void) -bson_iter_binary(const bson_iter_t *iter, bson_subtype_t *subtype, uint32_t *binary_len, const uint8_t **binary); - -BSON_EXPORT(void) -bson_iter_overwrite_binary(bson_iter_t *iter, bson_subtype_t subtype, uint32_t *binary_len, uint8_t **binary); - -BSON_EXPORT(bson_subtype_t) -bson_iter_binary_subtype(const bson_iter_t *iter); - -BSON_EXPORT(bool) -bson_iter_binary_equal(const bson_iter_t *iter_a, const bson_iter_t *iter_b); - - -BSON_EXPORT(const char *) -bson_iter_code(const bson_iter_t *iter, uint32_t *length); - - -/** - * bson_iter_code_unsafe: - * @iter: A bson_iter_t. - * @length: A location for the length of the resulting string. - * - * Like bson_iter_code() but performs no integrity checks. - * - * Returns: A string that should not be modified or freed. - */ -static BSON_INLINE const char * -bson_iter_code_unsafe(const bson_iter_t *iter, uint32_t *length) -{ - *length = bson_iter_utf8_len_unsafe(iter); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - return (const char *)(iter->raw + iter->d2); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END -} - - -BSON_EXPORT(const char *) -bson_iter_codewscope(const bson_iter_t *iter, uint32_t *length, uint32_t *scope_len, const uint8_t **scope); - - -BSON_EXPORT(void) -bson_iter_dbpointer(const bson_iter_t *iter, uint32_t *collection_len, const char **collection, const bson_oid_t **oid); - - -BSON_EXPORT(void) -bson_iter_document(const bson_iter_t *iter, uint32_t *document_len, const uint8_t **document); - - -BSON_EXPORT(double) -bson_iter_double(const bson_iter_t *iter); - -BSON_EXPORT(double) -bson_iter_as_double(const bson_iter_t *iter); - -/** - * bson_iter_double_unsafe: - * @iter: A bson_iter_t. - * - * Similar to bson_iter_double() but does not perform an integrity checking. - * - * Returns: A double. - */ -static BSON_INLINE double -bson_iter_double_unsafe(const bson_iter_t *iter) -{ - double val; - - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - memcpy(&val, iter->raw + iter->d1, sizeof(val)); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - return BSON_DOUBLE_FROM_LE(val); -} - - -BSON_EXPORT(bool) -bson_iter_init(bson_iter_t *iter, const bson_t *bson); - -BSON_EXPORT(bool) -bson_iter_init_from_data(bson_iter_t *iter, const uint8_t *data, size_t length); - - -BSON_EXPORT(bool) -bson_iter_init_find(bson_iter_t *iter, const bson_t *bson, const char *key); - - -BSON_EXPORT(bool) -bson_iter_init_find_w_len(bson_iter_t *iter, const bson_t *bson, const char *key, int keylen); - - -BSON_EXPORT(bool) -bson_iter_init_find_case(bson_iter_t *iter, const bson_t *bson, const char *key); - -BSON_EXPORT(bool) -bson_iter_init_from_data_at_offset( - bson_iter_t *iter, const uint8_t *data, size_t length, uint32_t offset, uint32_t keylen); - -BSON_EXPORT(int32_t) -bson_iter_int32(const bson_iter_t *iter); - - -/** - * bson_iter_int32_unsafe: - * @iter: A bson_iter_t. - * - * Similar to bson_iter_int32() but with no integrity checking. - * - * Returns: A 32-bit signed integer. - */ -static BSON_INLINE int32_t -bson_iter_int32_unsafe(const bson_iter_t *iter) -{ - uint32_t raw; - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - memcpy(&raw, iter->raw + iter->d1, sizeof(raw)); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - - const uint32_t native = BSON_UINT32_FROM_LE(raw); - - int32_t res; - memcpy(&res, &native, sizeof(res)); - return res; -} - - -BSON_EXPORT(int64_t) -bson_iter_int64(const bson_iter_t *iter); - - -BSON_EXPORT(int64_t) -bson_iter_as_int64(const bson_iter_t *iter); - - -/** - * bson_iter_int64_unsafe: - * @iter: a bson_iter_t. - * - * Similar to bson_iter_int64() but without integrity checking. - * - * Returns: A 64-bit signed integer. - */ -static BSON_INLINE int64_t -bson_iter_int64_unsafe(const bson_iter_t *iter) -{ - uint64_t raw; - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - memcpy(&raw, iter->raw + iter->d1, sizeof(raw)); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - - const uint64_t native = BSON_UINT64_FROM_LE(raw); - - int64_t res; - memcpy(&res, &native, sizeof(res)); - return res; -} - - -BSON_EXPORT(bool) -bson_iter_find(bson_iter_t *iter, const char *key); - - -BSON_EXPORT(bool) -bson_iter_find_w_len(bson_iter_t *iter, const char *key, int keylen); - - -BSON_EXPORT(bool) -bson_iter_find_case(bson_iter_t *iter, const char *key); - - -BSON_EXPORT(bool) -bson_iter_find_descendant(bson_iter_t *iter, const char *dotkey, bson_iter_t *descendant); - - -BSON_EXPORT(bool) -bson_iter_next(bson_iter_t *iter); - - -BSON_EXPORT(const bson_oid_t *) -bson_iter_oid(const bson_iter_t *iter); - - -/** - * bson_iter_oid_unsafe: - * @iter: A #bson_iter_t. - * - * Similar to bson_iter_oid() but performs no integrity checks. - * - * Returns: A #bson_oid_t that should not be modified or freed. - */ -static BSON_INLINE const bson_oid_t * -bson_iter_oid_unsafe(const bson_iter_t *iter) -{ - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - return (const bson_oid_t *)(iter->raw + iter->d1); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END -} - - -BSON_EXPORT(bool) -bson_iter_decimal128(const bson_iter_t *iter, bson_decimal128_t *dec); - - -/** - * bson_iter_decimal128_unsafe: - * @iter: A #bson_iter_t. - * - * Similar to bson_iter_decimal128() but performs no integrity checks. - * - * Returns: A #bson_decimal128_t. - */ -static BSON_INLINE void -bson_iter_decimal128_unsafe(const bson_iter_t *iter, bson_decimal128_t *dec) -{ - uint64_t low_le; - uint64_t high_le; - - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - memcpy(&low_le, iter->raw + iter->d1, sizeof(low_le)); - memcpy(&high_le, iter->raw + iter->d1 + 8, sizeof(high_le)); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - - dec->low = BSON_UINT64_FROM_LE(low_le); - dec->high = BSON_UINT64_FROM_LE(high_le); -} - - -BSON_EXPORT(const char *) -bson_iter_key(const bson_iter_t *iter); - -BSON_EXPORT(uint32_t) -bson_iter_key_len(const bson_iter_t *iter); - - -/** - * bson_iter_key_unsafe: - * @iter: A bson_iter_t. - * - * Similar to bson_iter_key() but performs no integrity checking. - * - * Returns: A string that should not be modified or freed. - */ -static BSON_INLINE const char * -bson_iter_key_unsafe(const bson_iter_t *iter) -{ - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - return (const char *)(iter->raw + iter->key); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END -} - - -BSON_EXPORT(const char *) -bson_iter_utf8(const bson_iter_t *iter, uint32_t *length); - - -/** - * bson_iter_utf8_unsafe: - * - * Similar to bson_iter_utf8() but performs no integrity checking. - * - * Returns: A string that should not be modified or freed. - */ -static BSON_INLINE const char * -bson_iter_utf8_unsafe(const bson_iter_t *iter, size_t *length) -{ - *length = bson_iter_utf8_len_unsafe(iter); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - return (const char *)(iter->raw + iter->d2); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END -} - - -BSON_EXPORT(char *) -bson_iter_dup_utf8(const bson_iter_t *iter, uint32_t *length); - - -BSON_EXPORT(int64_t) -bson_iter_date_time(const bson_iter_t *iter); - - -BSON_EXPORT(time_t) -bson_iter_time_t(const bson_iter_t *iter); - - -/** - * bson_iter_time_t_unsafe: - * @iter: A bson_iter_t. - * - * Similar to bson_iter_time_t() but performs no integrity checking. - * - * Returns: A time_t containing the number of seconds since UNIX epoch - * in UTC. - */ -static BSON_INLINE time_t -bson_iter_time_t_unsafe(const bson_iter_t *iter) -{ - return (time_t)(bson_iter_int64_unsafe(iter) / 1000); -} - - -BSON_EXPORT(void) -bson_iter_timeval(const bson_iter_t *iter, struct timeval *tv); - - -/** - * bson_iter_timeval_unsafe: - * @iter: A bson_iter_t. - * @tv: A struct timeval. - * - * Similar to bson_iter_timeval() but performs no integrity checking. - */ -static BSON_INLINE void -bson_iter_timeval_unsafe(const bson_iter_t *iter, struct timeval *tv) -{ - int64_t value = bson_iter_int64_unsafe(iter); -#ifdef BSON_OS_WIN32 - tv->tv_sec = (long)(value / 1000); - tv->tv_usec = (long)(value % 1000) * 1000; -#else - tv->tv_sec = (time_t)(value / 1000); - tv->tv_usec = (suseconds_t)(value % 1000) * 1000; -#endif -} - - -BSON_EXPORT(void) -bson_iter_timestamp(const bson_iter_t *iter, uint32_t *timestamp, uint32_t *increment); - - -BSON_EXPORT(bool) -bson_iter_bool(const bson_iter_t *iter); - - -/** - * bson_iter_bool_unsafe: - * @iter: A bson_iter_t. - * - * Similar to bson_iter_bool() but performs no integrity checking. - * - * Returns: true or false. - */ -static BSON_INLINE bool -bson_iter_bool_unsafe(const bson_iter_t *iter) -{ - char val; - - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - memcpy(&val, iter->raw + iter->d1, 1); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - return !!val; -} - - -BSON_EXPORT(bool) -bson_iter_as_bool(const bson_iter_t *iter); - - -BSON_EXPORT(const char *) -bson_iter_regex(const bson_iter_t *iter, const char **options); - - -BSON_EXPORT(const char *) -bson_iter_symbol(const bson_iter_t *iter, uint32_t *length); - - -BSON_EXPORT(bson_type_t) -bson_iter_type(const bson_iter_t *iter); - - -/** - * bson_iter_type_unsafe: - * @iter: A bson_iter_t. - * - * Similar to bson_iter_type() but performs no integrity checking. - * - * Returns: A bson_type_t. - */ -static BSON_INLINE bson_type_t -bson_iter_type_unsafe(const bson_iter_t *iter) -{ - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - return (bson_type_t)(iter->raw + iter->type)[0]; - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END -} - - -BSON_EXPORT(bool) -bson_iter_recurse(const bson_iter_t *iter, bson_iter_t *child); - - -BSON_EXPORT(void) -bson_iter_overwrite_int32(bson_iter_t *iter, int32_t value); - - -BSON_EXPORT(void) -bson_iter_overwrite_int64(bson_iter_t *iter, int64_t value); - - -BSON_EXPORT(void) -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_EXPORT(void) -bson_iter_overwrite_bool(bson_iter_t *iter, bool value); - - -BSON_EXPORT(void) -bson_iter_overwrite_oid(bson_iter_t *iter, const bson_oid_t *value); - - -BSON_EXPORT(void) -bson_iter_overwrite_timestamp(bson_iter_t *iter, uint32_t timestamp, uint32_t increment); - - -BSON_EXPORT(void) -bson_iter_overwrite_date_time(bson_iter_t *iter, int64_t value); - - -BSON_EXPORT(bool) -bson_iter_visit_all(bson_iter_t *iter, const bson_visitor_t *visitor, void *data); - -BSON_EXPORT(uint32_t) -bson_iter_offset(bson_iter_t *iter); - - -BSON_END_DECLS - - -#endif /* BSON_ITER_H */ diff --git a/bsonjs/bson/bson-json-private.h b/bsonjs/bson/bson-json-private.h deleted file mode 100644 index 4e9dc73..0000000 --- a/bsonjs/bson/bson-json-private.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#ifndef BSON_JSON_PRIVATE_H -#define BSON_JSON_PRIVATE_H - -#include // IWYU pragma: export - -struct _bson_json_opts_t { - bson_json_mode_t mode; - int32_t max_len; - bool is_outermost_array; -}; - - -#endif /* BSON_JSON_PRIVATE_H */ diff --git a/bsonjs/bson/bson-json.c b/bsonjs/bson/bson-json.c deleted file mode 100644 index 5bf64f5..0000000 --- a/bsonjs/bson/bson-json.c +++ /dev/null @@ -1,2398 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include - -#include -#include - -#include -#include - -#ifdef _WIN32 -#include -#include -#endif - -#ifndef _MSC_VER -#include -#endif - -#ifdef _MSC_VER -#define SSCANF sscanf_s -#else -#define SSCANF sscanf -#endif - -#define STACK_MAX 100 -#define BSON_JSON_DEFAULT_BUF_SIZE (1 << 14) -#define AT_LEAST_0(x) ((x) >= 0 ? (x) : 0) - - -#define READ_STATE_ENUM(ENUM) BSON_JSON_##ENUM, -#define GENERATE_STRING(STRING) #STRING, - -#define FOREACH_READ_STATE(RS) \ - RS(REGULAR) \ - RS(DONE) \ - RS(ERROR) \ - RS(IN_START_MAP) \ - RS(IN_BSON_TYPE) \ - RS(IN_BSON_TYPE_DATE_NUMBERLONG) \ - RS(IN_BSON_TYPE_DATE_ENDMAP) \ - RS(IN_BSON_TYPE_TIMESTAMP_STARTMAP) \ - RS(IN_BSON_TYPE_TIMESTAMP_VALUES) \ - RS(IN_BSON_TYPE_TIMESTAMP_ENDMAP) \ - RS(IN_BSON_TYPE_REGEX_STARTMAP) \ - RS(IN_BSON_TYPE_REGEX_VALUES) \ - RS(IN_BSON_TYPE_REGEX_ENDMAP) \ - RS(IN_BSON_TYPE_BINARY_VALUES) \ - RS(IN_BSON_TYPE_BINARY_ENDMAP) \ - RS(IN_BSON_TYPE_SCOPE_STARTMAP) \ - RS(IN_BSON_TYPE_DBPOINTER_STARTMAP) \ - RS(IN_SCOPE) \ - RS(IN_DBPOINTER) - -typedef enum { FOREACH_READ_STATE(READ_STATE_ENUM) } bson_json_read_state_t; - -static const char *read_state_names[] = {FOREACH_READ_STATE(GENERATE_STRING)}; - -#define BSON_STATE_ENUM(ENUM) BSON_JSON_LF_##ENUM, - -#define FOREACH_BSON_STATE(BS) \ - /* legacy {$regex: "...", $options: "..."} */ \ - BS(REGEX) \ - BS(OPTIONS) \ - /* modern $regularExpression: {pattern: "...", options: "..."} */ \ - BS(REGULAR_EXPRESSION_PATTERN) \ - BS(REGULAR_EXPRESSION_OPTIONS) \ - BS(CODE) \ - BS(SCOPE) \ - BS(OID) \ - BS(BINARY) \ - BS(TYPE) \ - BS(DATE) \ - BS(TIMESTAMP_T) \ - BS(TIMESTAMP_I) \ - BS(UNDEFINED) \ - BS(MINKEY) \ - BS(MAXKEY) \ - BS(INT32) \ - BS(INT64) \ - BS(DOUBLE) \ - BS(DECIMAL128) \ - BS(DBPOINTER) \ - BS(SYMBOL) \ - BS(UUID) - -typedef enum { FOREACH_BSON_STATE(BSON_STATE_ENUM) } bson_json_read_bson_state_t; - -static const char *bson_state_names[] = {FOREACH_BSON_STATE(GENERATE_STRING)}; - -typedef struct { - uint8_t *buf; - size_t n_bytes; - size_t len; -} bson_json_buf_t; - - -typedef enum { - BSON_JSON_FRAME_INITIAL = 0, - BSON_JSON_FRAME_ARRAY, - BSON_JSON_FRAME_DOC, - BSON_JSON_FRAME_SCOPE, - BSON_JSON_FRAME_DBPOINTER, -} bson_json_frame_type_t; - - -typedef struct { - int i; - bson_json_frame_type_t type; - bson_t bson; -} bson_json_stack_frame_t; - - -typedef union { - struct { - bool has_pattern; - bool has_options; - bool is_legacy; - } regex; - struct { - bool has_oid; - bson_oid_t oid; - } oid; - struct { - bool has_binary; - bool has_subtype; - bson_subtype_t type; - bool is_legacy; - } binary; - struct { - bool has_date; - int64_t date; - } date; - struct { - bool has_t; - bool has_i; - uint32_t t; - uint32_t i; - } timestamp; - struct { - bool has_undefined; - } undefined; - struct { - bool has_minkey; - } minkey; - struct { - bool has_maxkey; - } maxkey; - struct { - int32_t value; - } v_int32; - struct { - int64_t value; - } v_int64; - struct { - double value; - } v_double; - struct { - bson_decimal128_t value; - } v_decimal128; -} bson_json_bson_data_t; - - -/* collect info while parsing a {$code: "...", $scope: {...}} object */ -typedef struct { - bool has_code; - bool has_scope; - bool in_scope; - bson_json_buf_t key_buf; - bson_json_buf_t code_buf; -} bson_json_code_t; - - -static void -_bson_json_code_cleanup(bson_json_code_t *code_data) -{ - bson_free(code_data->key_buf.buf); - bson_free(code_data->code_buf.buf); -} - - -typedef struct { - bson_t *bson; - bson_json_stack_frame_t stack[STACK_MAX]; - int n; - const char *key; - bson_json_buf_t key_buf; - bson_json_buf_t unescaped; - bson_json_read_state_t read_state; - bson_json_read_bson_state_t bson_state; - bson_type_t bson_type; - bson_json_buf_t bson_type_buf[3]; - bson_json_bson_data_t bson_type_data; - bson_json_code_t code_data; - bson_json_buf_t dbpointer_key; -} bson_json_reader_bson_t; - - -typedef struct { - void *data; - bson_json_reader_cb cb; - bson_json_destroy_cb dcb; - uint8_t *buf; - size_t buf_size; - size_t bytes_read; - size_t bytes_parsed; - bool all_whitespace; -} bson_json_reader_producer_t; - - -struct _bson_json_reader_t { - bson_json_reader_producer_t producer; - bson_json_reader_bson_t bson; - jsonsl_t json; - ssize_t json_text_pos; - bool should_reset; - ssize_t advance; - bson_json_buf_t tok_accumulator; - bson_error_t *error; -}; - - -typedef struct { - int fd; - bool do_close; -} bson_json_reader_handle_fd_t; - - -/* forward decl */ -static void -_bson_json_save_map_key(bson_json_reader_bson_t *bson, const uint8_t *val, size_t len); - - -static void -_noop(void) -{ -} - -#define STACK_ELE(_delta, _name) (bson->stack[(_delta) + bson->n]._name) -#define STACK_BSON(_delta) (((_delta) + bson->n) == 0 ? bson->bson : &STACK_ELE(_delta, bson)) -#define STACK_BSON_PARENT STACK_BSON(-1) -#define STACK_BSON_CHILD STACK_BSON(0) -#define STACK_I STACK_ELE(0, i) -#define STACK_FRAME_TYPE STACK_ELE(0, type) -#define STACK_IS_ARRAY (STACK_FRAME_TYPE == BSON_JSON_FRAME_ARRAY) -#define STACK_IS_SCOPE (STACK_FRAME_TYPE == BSON_JSON_FRAME_SCOPE) -#define STACK_IS_DBPOINTER (STACK_FRAME_TYPE == BSON_JSON_FRAME_DBPOINTER) -#define FRAME_TYPE_HAS_BSON(_type) ((_type) == BSON_JSON_FRAME_SCOPE || (_type) == BSON_JSON_FRAME_DBPOINTER) -#define STACK_HAS_BSON FRAME_TYPE_HAS_BSON(STACK_FRAME_TYPE) -#define STACK_PUSH(frame_type) \ - do { \ - if (bson->n >= (STACK_MAX - 1)) { \ - return; \ - } \ - bson->n++; \ - mlib_diagnostic_push(); \ - mlib_disable_constant_conditional_expression_warnings(); \ - if (STACK_HAS_BSON) { \ - if (FRAME_TYPE_HAS_BSON(frame_type)) { \ - bson_reinit(STACK_BSON_CHILD); \ - } else { \ - bson_destroy(STACK_BSON_CHILD); \ - } \ - } else if (FRAME_TYPE_HAS_BSON(frame_type)) { \ - bson_init(STACK_BSON_CHILD); \ - } \ - mlib_diagnostic_pop(); \ - STACK_FRAME_TYPE = frame_type; \ - } while (0) -#define STACK_PUSH_ARRAY(statement) \ - do { \ - STACK_PUSH(BSON_JSON_FRAME_ARRAY); \ - STACK_I = 0; \ - if (bson->n != 0) { \ - statement; \ - } \ - } while (0) -#define STACK_PUSH_DOC(statement) \ - do { \ - STACK_PUSH(BSON_JSON_FRAME_DOC); \ - if (bson->n != 0) { \ - statement; \ - } \ - } while (0) -#define STACK_PUSH_SCOPE \ - do { \ - STACK_PUSH(BSON_JSON_FRAME_SCOPE); \ - bson->code_data.in_scope = true; \ - } while (0) -#define STACK_PUSH_DBPOINTER \ - do { \ - STACK_PUSH(BSON_JSON_FRAME_DBPOINTER); \ - } while (0) -#define STACK_POP_ARRAY(statement) \ - do { \ - if (!STACK_IS_ARRAY) { \ - return; \ - } \ - if (bson->n < 0) { \ - return; \ - } \ - if (bson->n > 0) { \ - statement; \ - } \ - bson->n--; \ - } while (0) -#define STACK_POP_DOC(statement) \ - do { \ - if (STACK_IS_ARRAY) { \ - return; \ - } \ - if (bson->n < 0) { \ - return; \ - } \ - if (bson->n > 0) { \ - statement; \ - } \ - bson->n--; \ - } while (0) -#define STACK_POP_SCOPE \ - do { \ - STACK_POP_DOC(_noop()); \ - bson->code_data.in_scope = false; \ - } while (0) -#define STACK_POP_DBPOINTER STACK_POP_DOC(_noop()) -#define BASIC_CB_PREAMBLE \ - const char *key; \ - size_t len; \ - bson_json_reader_bson_t *bson = &reader->bson; \ - _bson_json_read_fixup_key(bson); \ - key = bson->key; \ - len = bson->key_buf.len; \ - if (len > INT_MAX) { \ - _bson_json_read_set_error(reader, "Failed to read JSON. key size %zu is too large. Max is %d", len, INT_MAX); \ - return; \ - } \ - (void)0 -#define BASIC_CB_BAIL_IF_NOT_NORMAL(_type) \ - if (bson->read_state != BSON_JSON_REGULAR) { \ - _bson_json_read_set_error( \ - reader, "Invalid read of %s in state %s", (_type), read_state_names[bson->read_state]); \ - return; \ - } else if (!key) { \ - _bson_json_read_set_error( \ - reader, "Invalid read of %s without key in state %s", (_type), read_state_names[bson->read_state]); \ - return; \ - } else \ - (void)0 - -#define HANDLE_OPTION_KEY_COMPARE(_key) (len == strlen(_key) && memcmp(key, (_key), len) == 0) - -#define HANDLE_OPTION_TYPE_CHECK(_key, _type) \ - if (bson->bson_type && bson->bson_type != (_type)) { \ - _bson_json_read_set_error(reader, \ - "Invalid key \"%s\". Looking for values " \ - "for type \"%s\", got \"%s\"", \ - (_key), \ - _bson_json_type_name(bson->bson_type), \ - _bson_json_type_name(_type)); \ - return; \ - } \ - ((void)0) - -#define HANDLE_OPTION(_selection_statement, _key, _type, _state) \ - _selection_statement(HANDLE_OPTION_KEY_COMPARE(_key)) \ - { \ - HANDLE_OPTION_TYPE_CHECK(_key, _type); \ - bson->bson_type = (_type); \ - bson->bson_state = (_state); \ - } - - -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 = (bson_json_opts_t){ - .mode = mode, - .max_len = max_len, - .is_outermost_array = false, - }; - - return opts; -} - -void -bson_json_opts_destroy(bson_json_opts_t *opts) -{ - bson_free(opts); -} - -static void -_bson_json_read_set_error(bson_json_reader_t *reader, const char *fmt, ...) BSON_GNUC_PRINTF(2, 3); - - -static void -_bson_json_read_set_error(bson_json_reader_t *reader, /* IN */ - const char *fmt, /* IN */ - ...) -{ - va_list ap; - - if (reader->error) { - reader->error->domain = BSON_ERROR_JSON; - reader->error->code = BSON_JSON_ERROR_READ_INVALID_PARAM; - bson_set_error_category(reader->error, BSON_ERROR_CATEGORY); - - va_start(ap, fmt); - bson_vsnprintf(reader->error->message, sizeof reader->error->message, fmt, ap); - va_end(ap); - } - - reader->bson.read_state = BSON_JSON_ERROR; - jsonsl_stop(reader->json); -} - - -static void -_bson_json_read_corrupt(bson_json_reader_t *reader, const char *fmt, ...) BSON_GNUC_PRINTF(2, 3); - - -static void -_bson_json_read_corrupt(bson_json_reader_t *reader, /* IN */ - const char *fmt, /* IN */ - ...) -{ - va_list ap; - - if (reader->error) { - reader->error->domain = BSON_ERROR_JSON; - reader->error->code = BSON_JSON_ERROR_READ_CORRUPT_JS; - bson_set_error_category(reader->error, BSON_ERROR_CATEGORY); - - va_start(ap, fmt); - bson_vsnprintf(reader->error->message, sizeof reader->error->message, fmt, ap); - va_end(ap); - } - - reader->bson.read_state = BSON_JSON_ERROR; - jsonsl_stop(reader->json); -} - - -static void -_bson_json_buf_ensure(bson_json_buf_t *buf, /* IN */ - size_t len) /* IN */ -{ - if (buf->n_bytes < len) { - bson_free(buf->buf); - - buf->n_bytes = bson_next_power_of_two(len); - buf->buf = bson_malloc(buf->n_bytes); - } -} - - -static void -_bson_json_buf_set(bson_json_buf_t *buf, const void *from, size_t len) -{ - _bson_json_buf_ensure(buf, len + 1); - memcpy(buf->buf, from, len); - buf->buf[len] = '\0'; - buf->len = len; -} - - -static void -_bson_json_buf_append(bson_json_buf_t *buf, const void *from, size_t len) -{ - size_t len_with_null = len + 1; - - if (buf->len == 0) { - _bson_json_buf_ensure(buf, len_with_null); - } else if (buf->n_bytes < buf->len + len_with_null) { - buf->n_bytes = bson_next_power_of_two(buf->len + len_with_null); - buf->buf = bson_realloc(buf->buf, buf->n_bytes); - } - - memcpy(buf->buf + buf->len, from, len); - buf->len += len; - buf->buf[buf->len] = '\0'; -} - - -static const char * -_bson_json_type_name(bson_type_t type) -{ - switch (type) { - case BSON_TYPE_EOD: - return "end of document"; - case BSON_TYPE_DOUBLE: - return "double"; - case BSON_TYPE_UTF8: - return "utf-8"; - case BSON_TYPE_DOCUMENT: - return "document"; - case BSON_TYPE_ARRAY: - return "array"; - case BSON_TYPE_BINARY: - return "binary"; - case BSON_TYPE_UNDEFINED: - return "undefined"; - case BSON_TYPE_OID: - return "objectid"; - case BSON_TYPE_BOOL: - return "bool"; - case BSON_TYPE_DATE_TIME: - return "datetime"; - case BSON_TYPE_NULL: - return "null"; - case BSON_TYPE_REGEX: - return "regex"; - case BSON_TYPE_DBPOINTER: - return "dbpointer"; - case BSON_TYPE_CODE: - return "code"; - case BSON_TYPE_SYMBOL: - return "symbol"; - case BSON_TYPE_CODEWSCOPE: - return "code with scope"; - case BSON_TYPE_INT32: - return "int32"; - case BSON_TYPE_TIMESTAMP: - return "timestamp"; - case BSON_TYPE_INT64: - return "int64"; - case BSON_TYPE_DECIMAL128: - return "decimal128"; - case BSON_TYPE_MAXKEY: - return "maxkey"; - case BSON_TYPE_MINKEY: - return "minkey"; - default: - return ""; - } -} - - -static void -_bson_json_read_fixup_key(bson_json_reader_bson_t *bson) /* IN */ -{ - bson_json_read_state_t rs = bson->read_state; - - if (bson->n >= 0 && STACK_IS_ARRAY && rs == BSON_JSON_REGULAR) { - _bson_json_buf_ensure(&bson->key_buf, 12); - bson->key_buf.len = bson_uint32_to_string(STACK_I, &bson->key, (char *)bson->key_buf.buf, 12); - STACK_I++; - } -} - - -static void -_bson_json_read_null(bson_json_reader_t *reader) -{ - BASIC_CB_PREAMBLE; - BASIC_CB_BAIL_IF_NOT_NORMAL("null"); - - bson_append_null(STACK_BSON_CHILD, key, (int)len); -} - - -static void -_bson_json_read_boolean(bson_json_reader_t *reader, /* IN */ - int val) /* IN */ -{ - BASIC_CB_PREAMBLE; - - if (bson->read_state == BSON_JSON_IN_BSON_TYPE && bson->bson_state == BSON_JSON_LF_UNDEFINED) { - bson->bson_type_data.undefined.has_undefined = true; - return; - } - - BASIC_CB_BAIL_IF_NOT_NORMAL("boolean"); - - bson_append_bool(STACK_BSON_CHILD, key, (int)len, val); -} - - -/* sign is -1 or 1 */ -static void -_bson_json_read_integer(bson_json_reader_t *reader, uint64_t val, int64_t sign) -{ - bson_json_read_state_t rs; - bson_json_read_bson_state_t bs; - - BASIC_CB_PREAMBLE; - - if (sign == 1 && val > INT64_MAX) { - _bson_json_read_set_error(reader, "Number \"%" PRIu64 "\" is out of range", val); - - return; - } else if (sign == -1 && val > ((uint64_t)INT64_MAX + 1)) { - _bson_json_read_set_error(reader, "Number \"-%" PRIu64 "\" is out of range", val); - - return; - } - - rs = bson->read_state; - bs = bson->bson_state; - - if (rs == BSON_JSON_REGULAR) { - BASIC_CB_BAIL_IF_NOT_NORMAL("integer"); - - if (val <= INT32_MAX || (sign == -1 && val <= (uint64_t)INT32_MAX + 1)) { - bson_append_int32(STACK_BSON_CHILD, key, (int)len, (int32_t)((int64_t)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); - } - } else if (rs == BSON_JSON_IN_BSON_TYPE || rs == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES) { - switch (bs) { - case BSON_JSON_LF_DATE: - bson->bson_type_data.date.has_date = true; - bson->bson_type_data.date.date = sign * val; - break; - case BSON_JSON_LF_TIMESTAMP_T: - if (sign == -1) { - _bson_json_read_set_error(reader, "Invalid timestamp value: \"-%" PRIu64 "\"", val); - return; - } - - bson->bson_type_data.timestamp.has_t = true; - bson->bson_type_data.timestamp.t = (uint32_t)val; - break; - case BSON_JSON_LF_TIMESTAMP_I: - if (sign == -1) { - _bson_json_read_set_error(reader, "Invalid timestamp value: \"-%" PRIu64 "\"", val); - return; - } - - bson->bson_type_data.timestamp.has_i = true; - bson->bson_type_data.timestamp.i = (uint32_t)val; - break; - case BSON_JSON_LF_MINKEY: - if (sign == -1) { - _bson_json_read_set_error(reader, "Invalid MinKey value: \"-%" PRIu64 "\"", val); - return; - } else if (val != 1) { - _bson_json_read_set_error(reader, "Invalid MinKey value: \"%" PRIu64 "\"", val); - } - - bson->bson_type_data.minkey.has_minkey = true; - break; - case BSON_JSON_LF_MAXKEY: - if (sign == -1) { - _bson_json_read_set_error(reader, "Invalid MinKey value: \"-%" PRIu64 "\"", val); - return; - } else if (val != 1) { - _bson_json_read_set_error(reader, "Invalid MinKey value: \"%" PRIu64 "\"", val); - } - - bson->bson_type_data.maxkey.has_maxkey = true; - break; - case BSON_JSON_LF_INT32: - case BSON_JSON_LF_INT64: - _bson_json_read_set_error(reader, - "Invalid state for integer read: %s, " - "expected number as quoted string like \"123\"", - bson_state_names[bs]); - break; - case BSON_JSON_LF_REGEX: - case BSON_JSON_LF_OPTIONS: - case BSON_JSON_LF_REGULAR_EXPRESSION_PATTERN: - case BSON_JSON_LF_REGULAR_EXPRESSION_OPTIONS: - case BSON_JSON_LF_CODE: - case BSON_JSON_LF_SCOPE: - case BSON_JSON_LF_OID: - case BSON_JSON_LF_BINARY: - case BSON_JSON_LF_TYPE: - case BSON_JSON_LF_UUID: - case BSON_JSON_LF_UNDEFINED: - case BSON_JSON_LF_DOUBLE: - case BSON_JSON_LF_DECIMAL128: - case BSON_JSON_LF_DBPOINTER: - case BSON_JSON_LF_SYMBOL: - default: - _bson_json_read_set_error(reader, - "Unexpected integer %s%" PRIu64 " in type \"%s\"", - sign == -1 ? "-" : "", - val, - _bson_json_type_name(bson->bson_type)); - } - } else { - _bson_json_read_set_error( - reader, "Unexpected integer %s%" PRIu64 " in state \"%s\"", sign == -1 ? "-" : "", val, read_state_names[rs]); - } -} - - -static bool -_bson_json_parse_double(bson_json_reader_t *reader, const char *val, size_t vlen, double *d) -{ - errno = 0; - *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)) { - *d = NAN; - return true; - } else if (!_strnicmp(val, "infinity", vlen)) { - *d = pos_inf; - return true; - } else if (!_strnicmp(val, "-infinity", vlen)) { - *d = neg_inf; - return true; - } - } - - if ((*d == HUGE_VAL || *d == -HUGE_VAL) && errno == ERANGE) { - _bson_json_read_set_error(reader, "Number \"%.*s\" is out of range", (int)vlen, val); - - return false; - } -#else - /* not MSVC - set err on overflow, but avoid err for infinity */ - if ((*d == HUGE_VAL || *d == -HUGE_VAL) && errno == ERANGE && strncasecmp(val, "infinity", vlen) && - strncasecmp(val, "-infinity", vlen)) { - _bson_json_read_set_error(reader, "Number \"%.*s\" is out of range", (int)vlen, val); - - return false; - } - -#endif /* _MSC_VER */ - return true; -} - - -static void -_bson_json_read_double(bson_json_reader_t *reader, /* IN */ - double val) /* IN */ -{ - BASIC_CB_PREAMBLE; - BASIC_CB_BAIL_IF_NOT_NORMAL("double"); - - if (!bson_append_double(STACK_BSON_CHILD, key, (int)len, val)) { - _bson_json_read_set_error(reader, "Cannot append double value %g", val); - } -} - - -static bool -_bson_json_read_int64_or_set_error(bson_json_reader_t *reader, /* IN */ - const unsigned char *val, /* IN */ - size_t vlen, /* IN */ - int64_t *v64) /* OUT */ -{ - bson_json_reader_bson_t *bson = &reader->bson; - char *endptr = NULL; - - _bson_json_read_fixup_key(bson); - errno = 0; - *v64 = bson_ascii_strtoll((const char *)val, &endptr, 10); - - if (((*v64 == INT64_MIN) || (*v64 == INT64_MAX)) && (errno == ERANGE)) { - _bson_json_read_set_error(reader, "Number \"%s\" is out of range", val); - return false; - } - - if (endptr != ((const char *)val + vlen)) { - _bson_json_read_set_error(reader, "Number \"%s\" is invalid", val); - return false; - } - - return true; -} - -static bool -_unhexlify_uuid(const char *uuid, uint8_t *out, size_t max) -{ - unsigned int byte; - size_t x = 0; - int i = 0; - - BSON_ASSERT(strlen(uuid) == 32); - - while (SSCANF(&uuid[i], "%2x", &byte) == 1) { - if (x >= max) { - return false; - } - - out[x++] = (uint8_t)byte; - i += 2; - } - - return i == 32; -} - -/* parse a value for "base64", "subType", legacy "$binary" or "$type", or - * "$uuid" */ -static void -_bson_json_parse_binary_elem(bson_json_reader_t *reader, const char *val_w_null, size_t vlen) -{ - bson_json_read_bson_state_t bs; - bson_json_bson_data_t *data; - int binary_len; - - BASIC_CB_PREAMBLE; - - bs = bson->bson_state; - data = &bson->bson_type_data; - - if (bs == BSON_JSON_LF_BINARY) { - data->binary.has_binary = true; - binary_len = mcommon_b64_pton(val_w_null, NULL, 0); - if (binary_len < 0) { - _bson_json_read_set_error( - reader, "Invalid input string \"%s\", looking for base64-encoded binary", val_w_null); - } - - _bson_json_buf_ensure(&bson->bson_type_buf[0], (size_t)binary_len + 1); - 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", val_w_null); - } - - bson->bson_type_buf[0].len = (size_t)binary_len; - } else if (bs == BSON_JSON_LF_TYPE) { - data->binary.has_subtype = true; - - if (SSCANF(val_w_null, "%02x", &data->binary.type) != 1) { - if (!data->binary.is_legacy || data->binary.has_binary) { - /* misformatted subtype, like {$binary: {base64: "", subType: "x"}}, - * or legacy {$binary: "", $type: "x"} */ - _bson_json_read_set_error(reader, "Invalid input string \"%s\", looking for binary subtype", val_w_null); - } else { - /* actually a query operator: {x: {$type: "array"}}*/ - bson->read_state = BSON_JSON_REGULAR; - STACK_PUSH_DOC(bson_append_document_begin(STACK_BSON_PARENT, key, (int)len, STACK_BSON_CHILD)); - - bson_append_utf8(STACK_BSON_CHILD, "$type", 5, (const char *)val_w_null, (int)vlen); - } - } - } else if (bs == BSON_JSON_LF_UUID) { - int nread = 0; - char uuid[33]; - - data->binary.has_binary = true; - data->binary.has_subtype = true; - data->binary.type = BSON_SUBTYPE_UUID; - - /* Validate the UUID and extract relevant portions */ - /* We can't use %x here as it allows +, -, and 0x prefixes */ -#ifdef _MSC_VER - SSCANF(val_w_null, - "%8c-%4c-%4c-%4c-%12c%n", - &uuid[0], - 8, - &uuid[8], - 4, - &uuid[12], - 4, - &uuid[16], - 4, - &uuid[20], - 12, - &nread); -#else - SSCANF(val_w_null, "%8c-%4c-%4c-%4c-%12c%n", &uuid[0], &uuid[8], &uuid[12], &uuid[16], &uuid[20], &nread); -#endif - - uuid[32] = '\0'; - - if (nread != 36 || val_w_null[nread] != '\0') { - _bson_json_read_set_error(reader, - "Invalid input string \"%s\", looking for " - "a dash-separated UUID string", - val_w_null); - - return; - } - - binary_len = 16; - _bson_json_buf_ensure(&bson->bson_type_buf[0], (size_t)binary_len + 1); - - if (!_unhexlify_uuid(&uuid[0], bson->bson_type_buf[0].buf, (size_t)binary_len)) { - _bson_json_read_set_error(reader, - "Invalid input string \"%s\", looking for " - "a dash-separated UUID string", - val_w_null); - } - - bson->bson_type_buf[0].len = (size_t)binary_len; - } -} - -static bool -_bson_json_allow_embedded_nulls(bson_json_reader_t const *reader) -{ - const bson_json_read_state_t read_state = reader->bson.read_state; - const bson_json_read_bson_state_t bson_state = reader->bson.bson_state; - - if (read_state == BSON_JSON_IN_BSON_TYPE_REGEX_VALUES) { - if (bson_state == BSON_JSON_LF_REGULAR_EXPRESSION_PATTERN || - bson_state == BSON_JSON_LF_REGULAR_EXPRESSION_OPTIONS) { - /* Prohibit embedded NULL bytes for canonical extended regex: - * { $regularExpression: { pattern: "pattern", options: "options" } } - */ - return false; - } - } - - if (read_state == BSON_JSON_IN_BSON_TYPE) { - if (bson_state == BSON_JSON_LF_REGEX || bson_state == BSON_JSON_LF_OPTIONS) { - /* Prohibit embedded NULL bytes for legacy regex: - * { $regex: "pattern", $options: "options" } */ - return false; - } - } - - /* Embedded nulls are okay in any other context */ - return true; -} - -static void -_bson_json_read_string(bson_json_reader_t *reader, /* IN */ - const unsigned char *val, /* IN */ - size_t vlen) /* IN */ -{ - bson_json_read_state_t rs; - bson_json_read_bson_state_t bs; - const bool allow_null = _bson_json_allow_embedded_nulls(reader); - - BASIC_CB_PREAMBLE; - - rs = bson->read_state; - bs = bson->bson_state; - - if (!bson_utf8_validate((const char *)val, vlen, allow_null)) { - _bson_json_read_corrupt(reader, "invalid bytes in UTF8 string"); - return; - } - - if (rs == BSON_JSON_REGULAR) { - BASIC_CB_BAIL_IF_NOT_NORMAL("string"); - bson_append_utf8(STACK_BSON_CHILD, key, (int)len, (const char *)val, (int)vlen); - } else if (rs == BSON_JSON_IN_BSON_TYPE_SCOPE_STARTMAP || rs == BSON_JSON_IN_BSON_TYPE_DBPOINTER_STARTMAP) { - _bson_json_read_set_error(reader, "Invalid read of \"%s\" in state \"%s\"", val, read_state_names[rs]); - } else if (rs == BSON_JSON_IN_BSON_TYPE_BINARY_VALUES) { - const char *val_w_null; - _bson_json_buf_set(&bson->bson_type_buf[2], val, vlen); - val_w_null = (const char *)bson->bson_type_buf[2].buf; - - _bson_json_parse_binary_elem(reader, val_w_null, vlen); - } else if (rs == BSON_JSON_IN_BSON_TYPE || rs == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES || - rs == BSON_JSON_IN_BSON_TYPE_REGEX_VALUES || rs == BSON_JSON_IN_BSON_TYPE_DATE_NUMBERLONG) { - const char *val_w_null; - _bson_json_buf_set(&bson->bson_type_buf[2], val, vlen); - val_w_null = (const char *)bson->bson_type_buf[2].buf; - - switch (bs) { - case BSON_JSON_LF_REGEX: - bson->bson_type_data.regex.is_legacy = true; - /* FALL THROUGH */ - case BSON_JSON_LF_REGULAR_EXPRESSION_PATTERN: - bson->bson_type_data.regex.has_pattern = true; - _bson_json_buf_set(&bson->bson_type_buf[0], val, vlen); - break; - case BSON_JSON_LF_OPTIONS: - bson->bson_type_data.regex.is_legacy = true; - /* FALL THROUGH */ - case BSON_JSON_LF_REGULAR_EXPRESSION_OPTIONS: - bson->bson_type_data.regex.has_options = true; - _bson_json_buf_set(&bson->bson_type_buf[1], val, vlen); - break; - case BSON_JSON_LF_OID: - - if (vlen != 24) { - goto BAD_PARSE; - } - - bson->bson_type_data.oid.has_oid = true; - bson_oid_init_from_string(&bson->bson_type_data.oid.oid, val_w_null); - break; - case BSON_JSON_LF_BINARY: - case BSON_JSON_LF_TYPE: - bson->bson_type_data.binary.is_legacy = true; - /* FALL THROUGH */ - case BSON_JSON_LF_UUID: - _bson_json_parse_binary_elem(reader, val_w_null, vlen); - break; - case BSON_JSON_LF_INT32: { - int64_t v64; - if (!_bson_json_read_int64_or_set_error(reader, val, vlen, &v64)) { - /* the error is set, return and let the reader exit */ - return; - } - - if (v64 < INT32_MIN || v64 > INT32_MAX) { - goto BAD_PARSE; - } - - if (bson->read_state == BSON_JSON_IN_BSON_TYPE) { - bson->bson_type_data.v_int32.value = (int32_t)v64; - } else { - goto BAD_PARSE; - } - } break; - case BSON_JSON_LF_INT64: { - int64_t v64; - if (!_bson_json_read_int64_or_set_error(reader, val, vlen, &v64)) { - /* the error is set, return and let the reader exit */ - return; - } - - if (bson->read_state == BSON_JSON_IN_BSON_TYPE) { - bson->bson_type_data.v_int64.value = v64; - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_DATE_NUMBERLONG) { - bson->bson_type_data.date.has_date = true; - bson->bson_type_data.date.date = v64; - } else { - goto BAD_PARSE; - } - } break; - case BSON_JSON_LF_DOUBLE: { - if (!_bson_json_parse_double(reader, (const char *)val, vlen, &bson->bson_type_data.v_double.value)) { - /* the error is set, return and let the reader exit */ - return; - } - } break; - case BSON_JSON_LF_DATE: { - int64_t v64; - - if (!_bson_iso8601_date_parse((char *)val, (int)vlen, &v64, reader->error)) { - jsonsl_stop(reader->json); - } else { - bson->bson_type_data.date.has_date = true; - bson->bson_type_data.date.date = v64; - } - } break; - case BSON_JSON_LF_DECIMAL128: { - bson_decimal128_t decimal128; - - if (bson_decimal128_from_string(val_w_null, &decimal128) && bson->read_state == BSON_JSON_IN_BSON_TYPE) { - bson->bson_type_data.v_decimal128.value = decimal128; - } else { - goto BAD_PARSE; - } - } break; - case BSON_JSON_LF_CODE: - _bson_json_buf_set(&bson->code_data.code_buf, val, vlen); - break; - case BSON_JSON_LF_SYMBOL: - bson_append_symbol(STACK_BSON_CHILD, key, (int)len, (const char *)val, (int)vlen); - break; - case BSON_JSON_LF_SCOPE: - case BSON_JSON_LF_TIMESTAMP_T: - case BSON_JSON_LF_TIMESTAMP_I: - case BSON_JSON_LF_UNDEFINED: - case BSON_JSON_LF_MINKEY: - case BSON_JSON_LF_MAXKEY: - case BSON_JSON_LF_DBPOINTER: - default: - goto BAD_PARSE; - } - - return; - BAD_PARSE: - _bson_json_read_set_error( - reader, "Invalid input string \"%s\", looking for %s", val_w_null, bson_state_names[bs]); - } else { - _bson_json_read_set_error(reader, "Invalid state to look for string: %s", read_state_names[rs]); - } -} - - -static void -_bson_json_read_start_map(bson_json_reader_t *reader) /* IN */ -{ - BASIC_CB_PREAMBLE; - - if (bson->read_state == BSON_JSON_IN_BSON_TYPE) { - switch (bson->bson_state) { - case BSON_JSON_LF_DATE: - bson->read_state = BSON_JSON_IN_BSON_TYPE_DATE_NUMBERLONG; - break; - case BSON_JSON_LF_BINARY: - bson->read_state = BSON_JSON_IN_BSON_TYPE_BINARY_VALUES; - 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(mlib_in_range(int, len)); - STACK_PUSH_DOC(bson_append_document_begin(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; - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_SCOPE_STARTMAP) { - bson->read_state = BSON_JSON_IN_SCOPE; - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_DBPOINTER_STARTMAP) { - bson->read_state = BSON_JSON_IN_DBPOINTER; - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_REGEX_STARTMAP) { - bson->read_state = BSON_JSON_IN_BSON_TYPE_REGEX_VALUES; - } else { - bson->read_state = BSON_JSON_IN_START_MAP; - } - - /* silence some warnings */ - (void)len; - (void)key; -} - - -#define BSON_PRIVATE_SPECIAL_KEYS_XMACRO(X) \ - X(binary) \ - X(code) \ - X(date) \ - X(dbPointer) \ - X(maxKey) \ - X(minKey) \ - X(numberDecimal) \ - X(numberDouble) \ - X(numberInt) \ - X(numberLong) \ - X(oid) \ - X(options) \ - X(regex) \ - X(regularExpression) \ - X(scope) \ - X(symbol) \ - X(timestamp) \ - X(type) \ - X(undefined) \ - X(uuid) - - -static bool -_is_known_key(const char *key, size_t len) -{ -#define IS_KEY(k) \ - if (len == strlen("$" #k) && (0 == memcmp("$" #k, key, len))) { \ - return true; \ - } - BSON_PRIVATE_SPECIAL_KEYS_XMACRO(IS_KEY) -#undef IS_KEY - - return false; -} - -static void -_bson_json_save_map_key(bson_json_reader_bson_t *bson, const uint8_t *val, size_t len) -{ - _bson_json_buf_set(&bson->key_buf, val, len); - bson->key = (const char *)bson->key_buf.buf; -} - - -static void -_bson_json_read_code_or_scope_key(bson_json_reader_bson_t *bson, bool is_scope, const uint8_t *val, size_t len) -{ - bson_json_code_t *code = &bson->code_data; - - if (code->in_scope) { - /* we're reading something weirdly nested, e.g. we just read "$code" in - * "$scope: {x: {$code: {}}}". just create the subdoc within the scope. */ - bson->read_state = BSON_JSON_REGULAR; - STACK_PUSH_DOC( - bson_append_document_begin(STACK_BSON_PARENT, bson->key, (int)bson->key_buf.len, STACK_BSON_CHILD)); - _bson_json_save_map_key(bson, val, len); - } else { - if (!bson->code_data.key_buf.len) { - /* save the key, e.g. {"key": {"$code": "return x", "$scope":{"x":1}}}, - * in case it is overwritten while parsing scope sub-object */ - _bson_json_buf_set(&bson->code_data.key_buf, bson->key, bson->key_buf.len); - } - - if (is_scope) { - bson->bson_type = BSON_TYPE_CODEWSCOPE; - bson->read_state = BSON_JSON_IN_BSON_TYPE_SCOPE_STARTMAP; - bson->bson_state = BSON_JSON_LF_SCOPE; - bson->code_data.has_scope = true; - } else { - bson->bson_type = BSON_TYPE_CODE; - bson->bson_state = BSON_JSON_LF_CODE; - bson->code_data.has_code = true; - } - } -} - - -static void -_bson_json_bad_key_in_type(bson_json_reader_t *reader, /* IN */ - const uint8_t *val) /* IN */ -{ - bson_json_reader_bson_t *bson = &reader->bson; - - _bson_json_read_set_error( - reader, "Invalid key \"%s\". Looking for values for type \"%s\"", val, _bson_json_type_name(bson->bson_type)); -} - - -static void -_bson_json_read_map_key(bson_json_reader_t *reader, /* IN */ - const uint8_t *val, /* IN */ - size_t len) /* IN */ -{ - bson_json_reader_bson_t *bson = &reader->bson; - - if (!bson_utf8_validate((const char *)val, len, false /* allow null */)) { - _bson_json_read_corrupt(reader, "invalid bytes in UTF8 string"); - return; - } - - const char *const key = (const char *)val; - - if (bson->read_state == BSON_JSON_IN_START_MAP) { - if (len > 0 && key[0] == '$' && _is_known_key(key, len) && bson->n >= 0 /* key is in subdocument */) { - bson->read_state = BSON_JSON_IN_BSON_TYPE; - bson->bson_type = (bson_type_t)0; - memset(&bson->bson_type_data, 0, sizeof bson->bson_type_data); - } else { - bson->read_state = BSON_JSON_REGULAR; - STACK_PUSH_DOC( - bson_append_document_begin(STACK_BSON_PARENT, bson->key, (int)bson->key_buf.len, STACK_BSON_CHILD)); - } - } else if (bson->read_state == BSON_JSON_IN_SCOPE) { - /* we've read "key" in {$code: "", $scope: {key: ""}}*/ - bson->read_state = BSON_JSON_REGULAR; - STACK_PUSH_SCOPE; - _bson_json_save_map_key(bson, val, len); - } else if (bson->read_state == BSON_JSON_IN_DBPOINTER) { - /* we've read "$ref" or "$id" in {$dbPointer: {$ref: ..., $id: ...}} */ - bson->read_state = BSON_JSON_REGULAR; - STACK_PUSH_DBPOINTER; - _bson_json_save_map_key(bson, val, len); - } - - if (bson->read_state == BSON_JSON_IN_BSON_TYPE) { - 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 (HANDLE_OPTION_KEY_COMPARE("$timestamp")) - { - HANDLE_OPTION_TYPE_CHECK("$timestamp", BSON_TYPE_TIMESTAMP); - bson->bson_type = BSON_TYPE_TIMESTAMP; - bson->read_state = BSON_JSON_IN_BSON_TYPE_TIMESTAMP_STARTMAP; - } - else if (HANDLE_OPTION_KEY_COMPARE("$regularExpression")) - { - HANDLE_OPTION_TYPE_CHECK("$regularExpression", BSON_TYPE_REGEX); - bson->bson_type = BSON_TYPE_REGEX; - bson->read_state = BSON_JSON_IN_BSON_TYPE_REGEX_STARTMAP; - } - else if (HANDLE_OPTION_KEY_COMPARE("$dbPointer")) - { - HANDLE_OPTION_TYPE_CHECK("$dbPointer", BSON_TYPE_DBPOINTER); - - /* start parsing "key": {"$dbPointer": {...}}, save "key" for later */ - _bson_json_buf_set(&bson->dbpointer_key, bson->key, bson->key_buf.len); - - bson->bson_type = BSON_TYPE_DBPOINTER; - bson->read_state = BSON_JSON_IN_BSON_TYPE_DBPOINTER_STARTMAP; - } - else if (HANDLE_OPTION_KEY_COMPARE("$code")) - { - // "$code" may come after "$scope". - if (bson->bson_type != BSON_TYPE_CODEWSCOPE) { - HANDLE_OPTION_TYPE_CHECK("$code", BSON_TYPE_CODE); - } - _bson_json_read_code_or_scope_key(bson, false /* is_scope */, val, len); - } - else if (HANDLE_OPTION_KEY_COMPARE("$scope")) - { - // "$scope" may come after "$code". - if (bson->bson_type != BSON_TYPE_CODE) { - HANDLE_OPTION_TYPE_CHECK("$scope", BSON_TYPE_CODEWSCOPE); - } - _bson_json_read_code_or_scope_key(bson, true /* is_scope */, val, len); - } - else - { - _bson_json_bad_key_in_type(reader, val); - } - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_DATE_NUMBERLONG) { - 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) { - 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) { - 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) { - 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 { - _bson_json_save_map_key(bson, val, len); - } -} - - -static void -_bson_json_read_append_binary(bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ -{ - bson_json_bson_data_t *data = &bson->bson_type_data; - - if (data->binary.is_legacy) { - if (!data->binary.has_binary) { - _bson_json_read_set_error(reader, "Missing \"$binary\" after \"$type\" reading type \"binary\""); - return; - } else if (!data->binary.has_subtype) { - _bson_json_read_set_error(reader, "Missing \"$type\" after \"$binary\" reading type \"binary\""); - return; - } - } else { - if (!data->binary.has_binary) { - _bson_json_read_set_error(reader, "Missing \"base64\" after \"subType\" reading type \"binary\""); - return; - } else if (!data->binary.has_subtype) { - _bson_json_read_set_error(reader, "Missing \"subType\" after \"base64\" reading type \"binary\""); - return; - } - } - - if (!bson_append_binary(STACK_BSON_CHILD, - bson->key, - (int)bson->key_buf.len, - data->binary.type, - bson->bson_type_buf[0].buf, - (uint32_t)bson->bson_type_buf[0].len)) { - _bson_json_read_set_error(reader, "Error storing binary data"); - } -} - - -static void -_bson_json_read_append_regex(bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ -{ - bson_json_bson_data_t *data = &bson->bson_type_data; - if (data->regex.is_legacy) { - if (!data->regex.has_pattern) { - _bson_json_read_set_error(reader, "Missing \"$regex\" after \"$options\""); - return; - } - } else if (!data->regex.has_pattern) { - _bson_json_read_set_error(reader, "Missing \"pattern\" after \"options\" in regular expression"); - return; - } else if (!data->regex.has_options) { - _bson_json_read_set_error(reader, "Missing \"options\" after \"pattern\" in regular expression"); - return; - } - - if (!bson_append_regex(STACK_BSON_CHILD, - bson->key, - (int)bson->key_buf.len, - (char *)bson->bson_type_buf[0].buf, - (char *)bson->bson_type_buf[1].buf)) { - _bson_json_read_set_error(reader, "Error storing regex"); - } -} - - -static void -_bson_json_read_append_code(bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ -{ - bson_json_code_t *code_data; - char *code = NULL; - bson_t *scope = NULL; - bool r; - - code_data = &bson->code_data; - - BSON_ASSERT(!code_data->in_scope); - - if (!code_data->has_code) { - _bson_json_read_set_error(reader, "Missing $code after $scope"); - return; - } - - code = (char *)code_data->code_buf.buf; - - if (code_data->has_scope) { - scope = STACK_BSON(1); - } - - /* creates BSON "code" elem, or "code with scope" if scope is not NULL */ - r = bson_append_code_with_scope( - STACK_BSON_CHILD, (const char *)code_data->key_buf.buf, (int)code_data->key_buf.len, code, scope); - - if (!r) { - _bson_json_read_set_error(reader, "Error storing Javascript code"); - } - - /* keep the buffer but truncate it */ - code_data->key_buf.len = 0; - code_data->has_code = code_data->has_scope = false; -} - - -static void -_bson_json_read_append_dbpointer(bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ -{ - bson_t *db_pointer; - bson_iter_t iter; - const char *ns = NULL; - const bson_oid_t *oid = NULL; - bool r; - - BSON_ASSERT(reader->bson.dbpointer_key.buf); - - db_pointer = STACK_BSON(1); - if (!bson_iter_init(&iter, db_pointer)) { - _bson_json_read_set_error(reader, "Error storing DBPointer"); - return; - } - - while (bson_iter_next(&iter)) { - if (!strcmp(bson_iter_key(&iter), "$id")) { - if (!BSON_ITER_HOLDS_OID(&iter)) { - _bson_json_read_set_error(reader, "$dbPointer.$id must be like {\"$oid\": ...\"}"); - return; - } - - oid = bson_iter_oid(&iter); - } else if (!strcmp(bson_iter_key(&iter), "$ref")) { - if (!BSON_ITER_HOLDS_UTF8(&iter)) { - _bson_json_read_set_error(reader, "$dbPointer.$ref must be a string like \"db.collection\""); - return; - } - - ns = bson_iter_utf8(&iter, NULL); - } else { - _bson_json_read_set_error(reader, "$dbPointer contains invalid key: \"%s\"", bson_iter_key(&iter)); - return; - } - } - - if (!oid || !ns) { - _bson_json_read_set_error(reader, "$dbPointer requires both $id and $ref"); - return; - } - - r = bson_append_dbpointer( - STACK_BSON_CHILD, (char *)reader->bson.dbpointer_key.buf, (int)reader->bson.dbpointer_key.len, ns, oid); - - if (!r) { - _bson_json_read_set_error(reader, "Error storing DBPointer"); - } -} - - -static void -_bson_json_read_append_oid(bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ -{ - if (!bson_append_oid(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len, &bson->bson_type_data.oid.oid)) { - _bson_json_read_set_error(reader, "Error storing ObjectId"); - } -} - - -static void -_bson_json_read_append_date_time(bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ -{ - if (!bson_append_date_time(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len, bson->bson_type_data.date.date)) { - _bson_json_read_set_error(reader, "Error storing datetime"); - } -} - - -static void -_bson_json_read_append_timestamp(bson_json_reader_t *reader, /* IN */ - bson_json_reader_bson_t *bson) /* IN */ -{ - if (!bson->bson_type_data.timestamp.has_t) { - _bson_json_read_set_error(reader, "Missing t after $timestamp in BSON_TYPE_TIMESTAMP"); - return; - } else if (!bson->bson_type_data.timestamp.has_i) { - _bson_json_read_set_error(reader, "Missing i after $timestamp in BSON_TYPE_TIMESTAMP"); - return; - } - - bson_append_timestamp(STACK_BSON_CHILD, - bson->key, - (int)bson->key_buf.len, - bson->bson_type_data.timestamp.t, - bson->bson_type_data.timestamp.i); -} - - -static void -_bad_extended_json(bson_json_reader_t *reader) -{ - _bson_json_read_corrupt(reader, "Invalid MongoDB extended JSON"); -} - - -static void -_bson_json_read_end_map(bson_json_reader_t *reader) /* IN */ -{ - bson_json_reader_bson_t *bson = &reader->bson; - bool r = true; - - if (bson->read_state == BSON_JSON_IN_START_MAP) { - bson->read_state = BSON_JSON_REGULAR; - STACK_PUSH_DOC( - bson_append_document_begin(STACK_BSON_PARENT, bson->key, (int)bson->key_buf.len, STACK_BSON_CHILD)); - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_SCOPE_STARTMAP) { - bson->read_state = BSON_JSON_REGULAR; - STACK_PUSH_SCOPE; - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_DBPOINTER_STARTMAP) { - /* we've read last "}" in "{$dbPointer: {$id: ..., $ref: ...}}" */ - _bson_json_read_append_dbpointer(reader, bson); - bson->read_state = BSON_JSON_REGULAR; - return; - } - - if (bson->read_state == BSON_JSON_IN_BSON_TYPE) { - if (!bson->key) { - /* invalid, like {$numberLong: "1"} at the document top level */ - _bad_extended_json(reader); - return; - } - - bson->read_state = BSON_JSON_REGULAR; - switch (bson->bson_type) { - case BSON_TYPE_REGEX: - _bson_json_read_append_regex(reader, bson); - break; - case BSON_TYPE_CODE: - case BSON_TYPE_CODEWSCOPE: - /* we've read the closing "}" in "{$code: ..., $scope: ...}" */ - _bson_json_read_append_code(reader, bson); - break; - case BSON_TYPE_OID: - _bson_json_read_append_oid(reader, bson); - break; - case BSON_TYPE_BINARY: - _bson_json_read_append_binary(reader, bson); - break; - case BSON_TYPE_DATE_TIME: - _bson_json_read_append_date_time(reader, bson); - break; - case BSON_TYPE_UNDEFINED: - r = bson_append_undefined(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len); - break; - case BSON_TYPE_MINKEY: - r = bson_append_minkey(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len); - break; - case BSON_TYPE_MAXKEY: - r = bson_append_maxkey(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len); - break; - case BSON_TYPE_INT32: - r = bson_append_int32(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len, bson->bson_type_data.v_int32.value); - break; - case BSON_TYPE_INT64: - r = bson_append_int64(STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len, bson->bson_type_data.v_int64.value); - break; - case BSON_TYPE_DOUBLE: - r = bson_append_double( - STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len, bson->bson_type_data.v_double.value); - break; - case BSON_TYPE_DECIMAL128: - r = bson_append_decimal128( - STACK_BSON_CHILD, bson->key, (int)bson->key_buf.len, &bson->bson_type_data.v_decimal128.value); - break; - case BSON_TYPE_DBPOINTER: - /* shouldn't set type to DBPointer unless inside $dbPointer: {...} */ - _bson_json_read_set_error(reader, "Internal error: shouldn't be in state BSON_TYPE_DBPOINTER"); - break; - case BSON_TYPE_SYMBOL: - break; - case BSON_TYPE_EOD: - case BSON_TYPE_UTF8: - case BSON_TYPE_DOCUMENT: - case BSON_TYPE_ARRAY: - case BSON_TYPE_BOOL: - case BSON_TYPE_NULL: - case BSON_TYPE_TIMESTAMP: - default: - _bson_json_read_set_error( - reader, "Internal error: can't parse JSON wrapper for type \"%s\"", _bson_json_type_name(bson->bson_type)); - break; - } - - if (!r) { - _bson_json_read_set_error(reader, "Cannot append value at end of JSON object for key %s", bson->key); - } - - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_VALUES) { - if (!bson->key) { - _bad_extended_json(reader); - return; - } - - bson->read_state = BSON_JSON_IN_BSON_TYPE_TIMESTAMP_ENDMAP; - _bson_json_read_append_timestamp(reader, bson); - return; - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_REGEX_VALUES) { - if (!bson->key) { - _bad_extended_json(reader); - return; - } - - bson->read_state = BSON_JSON_IN_BSON_TYPE_REGEX_ENDMAP; - _bson_json_read_append_regex(reader, bson); - return; - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_BINARY_VALUES) { - if (!bson->key) { - _bad_extended_json(reader); - return; - } - - bson->read_state = BSON_JSON_IN_BSON_TYPE_BINARY_ENDMAP; - _bson_json_read_append_binary(reader, bson); - return; - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_TIMESTAMP_ENDMAP) { - bson->read_state = BSON_JSON_REGULAR; - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_REGEX_ENDMAP) { - bson->read_state = BSON_JSON_REGULAR; - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_BINARY_ENDMAP) { - bson->read_state = BSON_JSON_REGULAR; - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_DATE_NUMBERLONG) { - if (!bson->key) { - _bad_extended_json(reader); - return; - } - - bson->read_state = BSON_JSON_IN_BSON_TYPE_DATE_ENDMAP; - - _bson_json_read_append_date_time(reader, bson); - return; - } else if (bson->read_state == BSON_JSON_IN_BSON_TYPE_DATE_ENDMAP) { - bson->read_state = BSON_JSON_REGULAR; - } else if (bson->read_state == BSON_JSON_REGULAR) { - if (STACK_IS_SCOPE) { - bson->read_state = BSON_JSON_IN_BSON_TYPE; - bson->bson_type = BSON_TYPE_CODE; - STACK_POP_SCOPE; - } else if (STACK_IS_DBPOINTER) { - bson->read_state = BSON_JSON_IN_BSON_TYPE_DBPOINTER_STARTMAP; - STACK_POP_DBPOINTER; - } else { - STACK_POP_DOC(bson_append_document_end(STACK_BSON_PARENT, STACK_BSON_CHILD)); - } - - if (bson->n == -1) { - bson->read_state = BSON_JSON_DONE; - } - } else if (bson->read_state == BSON_JSON_IN_SCOPE) { - /* empty $scope */ - BSON_ASSERT(bson->code_data.has_scope); - STACK_PUSH_SCOPE; - STACK_POP_SCOPE; - bson->read_state = BSON_JSON_IN_BSON_TYPE; - bson->bson_type = BSON_TYPE_CODE; - } else if (bson->read_state == BSON_JSON_IN_DBPOINTER) { - /* empty $dbPointer??? */ - _bson_json_read_set_error(reader, "Empty $dbPointer"); - } else { - _bson_json_read_set_error(reader, "Invalid state \"%s\"", read_state_names[bson->read_state]); - } -} - - -static void -_bson_json_read_start_array(bson_json_reader_t *reader) /* IN */ -{ - const char *key; - size_t len; - bson_json_reader_bson_t *bson = &reader->bson; - - if (bson->read_state != BSON_JSON_REGULAR) { - _bson_json_read_set_error(reader, "Invalid read of \"[\" in state \"%s\"", read_state_names[bson->read_state]); - return; - } - - if (bson->n == -1) { - STACK_PUSH_ARRAY(_noop()); - } else { - _bson_json_read_fixup_key(bson); - key = bson->key; - len = bson->key_buf.len; - - STACK_PUSH_ARRAY(bson_append_array_unsafe_begin(STACK_BSON_PARENT, key, (int)len, STACK_BSON_CHILD)); - } -} - - -static void -_bson_json_read_end_array(bson_json_reader_t *reader) /* IN */ -{ - bson_json_reader_bson_t *bson = &reader->bson; - - if (bson->read_state != BSON_JSON_REGULAR) { - _bson_json_read_set_error(reader, "Invalid read of \"]\" in state \"%s\"", read_state_names[bson->read_state]); - return; - } - - STACK_POP_ARRAY(bson_append_array_end(STACK_BSON_PARENT, STACK_BSON_CHILD)); - if (bson->n == -1) { - bson->read_state = BSON_JSON_DONE; - } -} - - -/* put unescaped text in reader->bson.unescaped, or set reader->error. - * json_text has length len and it is not null-terminated. */ -static bool -_bson_json_unescape(bson_json_reader_t *reader, struct jsonsl_state_st *state, const char *json_text, ssize_t len) -{ - bson_json_reader_bson_t *reader_bson; - jsonsl_error_t err; - - reader_bson = &reader->bson; - - /* add 1 for NULL */ - _bson_json_buf_ensure(&reader_bson->unescaped, (size_t)len + 1); - - /* length of unescaped str is always <= len */ - reader_bson->unescaped.len = - jsonsl_util_unescape(json_text, (char *)reader_bson->unescaped.buf, (size_t)len, NULL, &err); - - if (err != JSONSL_ERROR_SUCCESS) { - bson_set_error(reader->error, - BSON_ERROR_JSON, - BSON_JSON_ERROR_READ_CORRUPT_JS, - "error near position %d: \"%s\"", - (int)state->pos_begin, - jsonsl_strerror(err)); - return false; - } - - reader_bson->unescaped.buf[reader_bson->unescaped.len] = '\0'; - - return true; -} - - -/* read the buffered JSON plus new data, and fill out @len with its length */ -static const char * -_get_json_text(jsonsl_t json, /* IN */ - struct jsonsl_state_st *state, /* IN */ - const char *buf /* IN */, - ssize_t *len /* OUT */) -{ - bson_json_reader_t *reader; - ssize_t bytes_available; - - reader = (bson_json_reader_t *)json->data; - - BSON_ASSERT(state->pos_cur > state->pos_begin); - - *len = (ssize_t)(state->pos_cur - state->pos_begin); - - bytes_available = buf - json->base; - - if (*len <= bytes_available) { - /* read directly from stream, not from saved JSON */ - return buf - (size_t)*len; - } else { - /* combine saved text with new data from the jsonsl_t */ - ssize_t append = buf - json->base; - - if (append > 0) { - _bson_json_buf_append(&reader->tok_accumulator, buf - append, (size_t)append); - } - - return (const char *)reader->tok_accumulator.buf; - } -} - - -static void -_push_callback(jsonsl_t json, jsonsl_action_t action, struct jsonsl_state_st *state, const char *buf) -{ - 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: - case JSONSL_T_SPECIAL: - case JSONSL_T_UESCAPE: - reader->json_text_pos = state->pos_begin; - break; - case JSONSL_T_OBJECT: - _bson_json_read_start_map(reader); - break; - case JSONSL_T_LIST: - _bson_json_read_start_array(reader); - break; - default: - break; - } -} - - -static void -_pop_callback(jsonsl_t json, jsonsl_action_t action, struct jsonsl_state_st *state, const char *buf) -{ - bson_json_reader_t *reader; - bson_json_reader_bson_t *reader_bson; - ssize_t len; - double d; - const char *obj_text; - - BSON_UNUSED(action); - - reader = (bson_json_reader_t *)json->data; - reader_bson = &reader->bson; - - switch (state->type) { - case JSONSL_T_HKEY: - case JSONSL_T_STRING: - obj_text = _get_json_text(json, state, buf, &len); - BSON_ASSERT(obj_text[0] == '"'); - - /* remove start/end quotes, replace backslash-escapes, null-terminate */ - /* you'd think it would be faster to check if state->nescapes > 0 first, - * but tests show no improvement */ - if (!_bson_json_unescape(reader, state, obj_text + 1, len - 1)) { - /* reader->error is set */ - jsonsl_stop(json); - break; - } - - if (state->type == JSONSL_T_HKEY) { - _bson_json_read_map_key(reader, reader_bson->unescaped.buf, reader_bson->unescaped.len); - } else { - _bson_json_read_string(reader, reader_bson->unescaped.buf, reader_bson->unescaped.len); - } - break; - case JSONSL_T_OBJECT: - _bson_json_read_end_map(reader); - break; - case JSONSL_T_LIST: - _bson_json_read_end_array(reader); - break; - case JSONSL_T_SPECIAL: - obj_text = _get_json_text(json, state, buf, &len); - if (state->special_flags & JSONSL_SPECIALf_NUMNOINT) { - if (_bson_json_parse_double(reader, obj_text, (size_t)len, &d)) { - _bson_json_read_double(reader, d); - } - } else if (state->special_flags & JSONSL_SPECIALf_NUMERIC) { - /* jsonsl puts the unsigned value in state->nelem */ - _bson_json_read_integer(reader, state->nelem, state->special_flags & JSONSL_SPECIALf_SIGNED ? -1 : 1); - } else if (state->special_flags & JSONSL_SPECIALf_BOOLEAN) { - _bson_json_read_boolean(reader, obj_text[0] == 't' ? 1 : 0); - } else if (state->special_flags & JSONSL_SPECIALf_NULL) { - _bson_json_read_null(reader); - } - break; - default: - break; - } - - reader->json_text_pos = -1; - reader->tok_accumulator.len = 0; -} - - -static int -_error_callback(jsonsl_t json, jsonsl_error_t err, struct jsonsl_state_st *state, char *errat) -{ - 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; - reader->advance = errat - json->base; - return 0; - } - - bson_set_error(reader->error, - BSON_ERROR_JSON, - BSON_JSON_ERROR_READ_CORRUPT_JS, - "Got parse error at \"%c\", position %d: \"%s\"", - *errat, - (int)json->pos, - jsonsl_strerror(err)); - - return 0; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_json_reader_read -- - * - * Read the next json document from @reader and write its value - * into @bson. @bson will be allocated as part of this process. - * - * @bson MUST be initialized before calling this function as it - * will not be initialized automatically. The reasoning for this - * is so that you can chain together bson_json_reader_t with - * other components like bson_writer_t. - * - * Returns: - * 1 if successful and data was read. - * 0 if successful and no data was read. - * -1 if there was an error and @error is set. - * - * Side effects: - * @error may be set. - * - *-------------------------------------------------------------------------- - */ - -int -bson_json_reader_read(bson_json_reader_t *reader, /* IN */ - bson_t *bson, /* IN */ - bson_error_t *error) /* OUT */ -{ - bson_json_reader_producer_t *p; - ssize_t start_pos; - ssize_t r; - ssize_t buf_offset; - ssize_t accum; - bson_error_t error_tmp; - int ret = 0; - - BSON_ASSERT(reader); - BSON_ASSERT(bson); - - p = &reader->producer; - - reader->bson.bson = bson; - reader->bson.n = -1; - reader->bson.read_state = BSON_JSON_REGULAR; - reader->error = error ? error : &error_tmp; - memset(reader->error, 0, sizeof(bson_error_t)); - - for (;;) { - start_pos = reader->json->pos; - - if (p->bytes_read > 0) { - /* leftover data from previous JSON doc in the stream */ - r = p->bytes_read; - } else { - /* read a chunk of bytes by executing the callback */ - r = p->cb(p->data, p->buf, p->buf_size); - } - - if (r < 0) { - if (error) { - bson_set_error(error, BSON_ERROR_JSON, BSON_JSON_ERROR_READ_CB_FAILURE, "reader cb failed"); - } - ret = -1; - goto cleanup; - } else if (r == 0) { - break; - } else { - ret = 1; - p->bytes_read = (size_t)r; - - jsonsl_feed(reader->json, (const jsonsl_char_t *)p->buf, (size_t)r); - - if (reader->should_reset) { - /* end of a document */ - jsonsl_reset(reader->json); - reader->should_reset = false; - - /* advance past already-parsed data */ - memmove(p->buf, p->buf + reader->advance, r - reader->advance); - p->bytes_read -= reader->advance; - ret = 1; - goto cleanup; - } - - if (reader->error->domain) { - ret = -1; - goto cleanup; - } - - /* accumulate a key or string value */ - if (reader->json_text_pos != -1) { - if (mlib_cmp(reader->json_text_pos, <, reader->json->pos)) { - BSON_ASSERT(mlib_in_range(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); - _bson_json_buf_append(&reader->tok_accumulator, p->buf + buf_offset, (size_t)accum); - } - } - - p->bytes_read = 0; - } - } - -cleanup: - if (ret == 1 && reader->bson.read_state != BSON_JSON_DONE) { - /* data ended in the middle */ - _bson_json_read_corrupt(reader, "%s", "Incomplete JSON"); - return -1; - } - - return ret; -} - - -bson_json_reader_t * -bson_json_reader_new(void *data, /* IN */ - bson_json_reader_cb cb, /* IN */ - bson_json_destroy_cb dcb, /* IN */ - bool allow_multiple, /* unused */ - size_t buf_size) /* IN */ -{ - bson_json_reader_t *r; - bson_json_reader_producer_t *p; - - 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; - r->json->action_callback_POP = _pop_callback; - r->json->data = r; - r->json_text_pos = -1; - jsonsl_enable_all_callbacks(r->json); - - p = &r->producer; - - p->data = data; - p->cb = cb; - p->dcb = dcb; - p->buf_size = buf_size ? buf_size : BSON_JSON_DEFAULT_BUF_SIZE; - p->buf = bson_malloc(p->buf_size); - - return r; -} - - -void -bson_json_reader_destroy(bson_json_reader_t *reader) /* IN */ -{ - int i; - bson_json_reader_producer_t *p; - bson_json_reader_bson_t *b; - - if (!reader) { - return; - } - - p = &reader->producer; - b = &reader->bson; - - if (reader->producer.dcb) { - reader->producer.dcb(reader->producer.data); - } - - bson_free(p->buf); - bson_free(b->key_buf.buf); - bson_free(b->unescaped.buf); - bson_free(b->dbpointer_key.buf); - - /* destroy each bson_t initialized in parser stack frames */ - for (i = 1; i < STACK_MAX; i++) { - if (b->stack[i].type == BSON_JSON_FRAME_INITIAL) { - /* highest the stack grew */ - break; - } - - if (FRAME_TYPE_HAS_BSON(b->stack[i].type)) { - bson_destroy(&b->stack[i].bson); - } - } - - for (i = 0; i < 3; i++) { - bson_free(b->bson_type_buf[i].buf); - } - - _bson_json_code_cleanup(&b->code_data); - - jsonsl_destroy(reader->json); - bson_free(reader->tok_accumulator.buf); - bson_free(reader); -} - - -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; - size_t bytes_parsed; -} bson_json_data_reader_t; - - -static ssize_t -_bson_json_data_reader_cb(void *_ctx, uint8_t *buf, size_t len) -{ - size_t bytes; - bson_json_data_reader_t *ctx = (bson_json_data_reader_t *)_ctx; - - if (!ctx->data) { - return -1; - } - - bytes = BSON_MIN(len, ctx->len - ctx->bytes_parsed); - - memcpy(buf, ctx->data + ctx->bytes_parsed, bytes); - - ctx->bytes_parsed += bytes; - - return bytes; -} - - -bson_json_reader_t * -bson_json_data_reader_new(bool allow_multiple, /* IN */ - size_t size) /* IN */ -{ - bson_json_data_reader_t *dr = bson_malloc0(sizeof *dr); - - return bson_json_reader_new(dr, &_bson_json_data_reader_cb, &bson_free, allow_multiple, size); -} - - -void -bson_json_data_reader_ingest(bson_json_reader_t *reader, /* IN */ - const uint8_t *data, /* IN */ - size_t len) /* IN */ -{ - bson_json_data_reader_t *ctx = (bson_json_data_reader_t *)reader->producer.data; - - ctx->data = data; - ctx->len = len; - ctx->bytes_parsed = 0; -} - - -bson_t * -bson_new_from_json(const uint8_t *data, /* IN */ - ssize_t len, /* IN */ - bson_error_t *error) /* OUT */ -{ - bson_json_reader_t *reader; - bson_t *bson; - int r; - - BSON_ASSERT(data); - - if (len < 0) { - len = (ssize_t)strlen((const char *)data); - } - - bson = bson_new(); - reader = bson_json_data_reader_new(false, BSON_JSON_DEFAULT_BUF_SIZE); - bson_json_data_reader_ingest(reader, data, len); - r = bson_json_reader_read(reader, bson, error); - bson_json_reader_destroy(reader); - - if (r == 0) { - bson_set_error(error, BSON_ERROR_JSON, BSON_JSON_ERROR_READ_INVALID_PARAM, "Empty JSON string"); - } - - if (r != 1) { - bson_destroy(bson); - return NULL; - } - - return bson; -} - - -bool -bson_init_from_json(bson_t *bson, /* OUT */ - const char *data, /* IN */ - ssize_t len, /* IN */ - bson_error_t *error) /* OUT */ -{ - bson_json_reader_t *reader; - int r; - - BSON_ASSERT(bson); - BSON_ASSERT(data); - - if (len < 0) { - len = strlen(data); - } - - bson_init(bson); - - reader = bson_json_data_reader_new(false, BSON_JSON_DEFAULT_BUF_SIZE); - bson_json_data_reader_ingest(reader, (const uint8_t *)data, len); - r = bson_json_reader_read(reader, bson, error); - bson_json_reader_destroy(reader); - - if (r == 0) { - bson_set_error(error, BSON_ERROR_JSON, BSON_JSON_ERROR_READ_INVALID_PARAM, "Empty JSON string"); - } - - if (r != 1) { - bson_destroy(bson); - return false; - } - - return true; -} - - -static void -_bson_json_reader_handle_fd_destroy(void *handle) /* IN */ -{ - bson_json_reader_handle_fd_t *fd = handle; - - if (fd) { - if ((fd->fd != -1) && fd->do_close) { -#ifdef _WIN32 - _close(fd->fd); -#else - close(fd->fd); -#endif - } - bson_free(fd); - } -} - - -static ssize_t -_bson_json_reader_handle_fd_read(void *handle, /* IN */ - uint8_t *buf, /* IN */ - size_t len) /* IN */ -{ - bson_json_reader_handle_fd_t *fd = handle; - ssize_t ret = -1; - - if (fd && (fd->fd != -1)) { - again: -#ifdef BSON_OS_WIN32 - ret = _read(fd->fd, buf, (unsigned int)len); -#else - ret = read(fd->fd, buf, len); -#endif - if ((ret == -1) && (errno == EAGAIN)) { - goto again; - } - } - - return ret; -} - - -bson_json_reader_t * -bson_json_reader_new_from_fd(int fd, /* IN */ - bool close_on_destroy) /* IN */ -{ - bson_json_reader_handle_fd_t *handle; - - BSON_ASSERT(fd != -1); - - handle = bson_malloc0(sizeof *handle); - handle->fd = fd; - handle->do_close = close_on_destroy; - - return bson_json_reader_new( - handle, _bson_json_reader_handle_fd_read, _bson_json_reader_handle_fd_destroy, true, BSON_JSON_DEFAULT_BUF_SIZE); -} - - -bson_json_reader_t * -bson_json_reader_new_from_file(const char *path, /* IN */ - bson_error_t *error) /* OUT */ -{ - char errmsg_buf[BSON_ERROR_BUFFER_SIZE]; - char *errmsg; - int fd = -1; - - BSON_ASSERT(path); - -#ifdef BSON_OS_WIN32 - _sopen_s(&fd, path, (_O_RDONLY | _O_BINARY), _SH_DENYNO, _S_IREAD); -#else - fd = open(path, O_RDONLY); -#endif - - if (fd == -1) { - errmsg = bson_strerror_r(errno, errmsg_buf, sizeof errmsg_buf); - bson_set_error(error, BSON_ERROR_READER, BSON_ERROR_READER_BADFD, "%s", errmsg); - return NULL; - } - - return bson_json_reader_new_from_fd(fd, true); -} diff --git a/bsonjs/bson/bson-json.h b/bsonjs/bson/bson-json.h deleted file mode 100644 index 6fe8adb..0000000 --- a/bsonjs/bson/bson-json.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_JSON_H -#define BSON_JSON_H - - -#include - - -BSON_BEGIN_DECLS - - -typedef struct _bson_json_reader_t bson_json_reader_t; - - -typedef enum { - BSON_JSON_ERROR_READ_CORRUPT_JS = 1, - BSON_JSON_ERROR_READ_INVALID_PARAM, - BSON_JSON_ERROR_READ_CB_FAILURE, -} bson_json_error_code_t; - - -/** - * BSON_MAX_LEN_UNLIMITED - * - * Denotes unlimited length limit when converting BSON to JSON. - */ -#define BSON_MAX_LEN_UNLIMITED -1 - -/** - * bson_json_mode_t: - * - * This enumeration contains the different modes to serialize BSON into extended - * JSON. - */ -typedef enum { - BSON_JSON_MODE_LEGACY, - BSON_JSON_MODE_CANONICAL, - BSON_JSON_MODE_RELAXED, -} bson_json_mode_t; - - -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_CALL *bson_json_reader_cb)(void *handle, uint8_t *buf, size_t count); -typedef void(BSON_CALL *bson_json_destroy_cb)(void *handle); - - -BSON_EXPORT(bson_json_reader_t *) -bson_json_reader_new( - void *data, bson_json_reader_cb cb, bson_json_destroy_cb dcb, bool allow_multiple, size_t buf_size); -BSON_EXPORT(bson_json_reader_t *) -bson_json_reader_new_from_fd(int fd, bool close_on_destroy); -BSON_EXPORT(bson_json_reader_t *) -bson_json_reader_new_from_file(const char *filename, bson_error_t *error); -BSON_EXPORT(void) -bson_json_reader_destroy(bson_json_reader_t *reader); -BSON_EXPORT(int) -bson_json_reader_read(bson_json_reader_t *reader, bson_t *bson, bson_error_t *error); -BSON_EXPORT(bson_json_reader_t *) -bson_json_data_reader_new(bool allow_multiple, size_t size); -BSON_EXPORT(void) -bson_json_data_reader_ingest(bson_json_reader_t *reader, const uint8_t *data, size_t len); - - -BSON_END_DECLS - - -#endif /* BSON_JSON_H */ diff --git a/bsonjs/bson/bson-keys.c b/bsonjs/bson/bson-keys.c deleted file mode 100644 index 0af74cd..0000000 --- a/bsonjs/bson/bson-keys.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include - -#include - -#include - -#include - - -static const char *gUint32Strs[] = { - "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", - "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", - "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", - "96", "97", "98", "99", "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", "110", "111", - "112", "113", "114", "115", "116", "117", "118", "119", "120", "121", "122", "123", "124", "125", "126", "127", - "128", "129", "130", "131", "132", "133", "134", "135", "136", "137", "138", "139", "140", "141", "142", "143", - "144", "145", "146", "147", "148", "149", "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", - "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", "170", "171", "172", "173", "174", "175", - "176", "177", "178", "179", "180", "181", "182", "183", "184", "185", "186", "187", "188", "189", "190", "191", - "192", "193", "194", "195", "196", "197", "198", "199", "200", "201", "202", "203", "204", "205", "206", "207", - "208", "209", "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", "220", "221", "222", "223", - "224", "225", "226", "227", "228", "229", "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", - "240", "241", "242", "243", "244", "245", "246", "247", "248", "249", "250", "251", "252", "253", "254", "255", - "256", "257", "258", "259", "260", "261", "262", "263", "264", "265", "266", "267", "268", "269", "270", "271", - "272", "273", "274", "275", "276", "277", "278", "279", "280", "281", "282", "283", "284", "285", "286", "287", - "288", "289", "290", "291", "292", "293", "294", "295", "296", "297", "298", "299", "300", "301", "302", "303", - "304", "305", "306", "307", "308", "309", "310", "311", "312", "313", "314", "315", "316", "317", "318", "319", - "320", "321", "322", "323", "324", "325", "326", "327", "328", "329", "330", "331", "332", "333", "334", "335", - "336", "337", "338", "339", "340", "341", "342", "343", "344", "345", "346", "347", "348", "349", "350", "351", - "352", "353", "354", "355", "356", "357", "358", "359", "360", "361", "362", "363", "364", "365", "366", "367", - "368", "369", "370", "371", "372", "373", "374", "375", "376", "377", "378", "379", "380", "381", "382", "383", - "384", "385", "386", "387", "388", "389", "390", "391", "392", "393", "394", "395", "396", "397", "398", "399", - "400", "401", "402", "403", "404", "405", "406", "407", "408", "409", "410", "411", "412", "413", "414", "415", - "416", "417", "418", "419", "420", "421", "422", "423", "424", "425", "426", "427", "428", "429", "430", "431", - "432", "433", "434", "435", "436", "437", "438", "439", "440", "441", "442", "443", "444", "445", "446", "447", - "448", "449", "450", "451", "452", "453", "454", "455", "456", "457", "458", "459", "460", "461", "462", "463", - "464", "465", "466", "467", "468", "469", "470", "471", "472", "473", "474", "475", "476", "477", "478", "479", - "480", "481", "482", "483", "484", "485", "486", "487", "488", "489", "490", "491", "492", "493", "494", "495", - "496", "497", "498", "499", "500", "501", "502", "503", "504", "505", "506", "507", "508", "509", "510", "511", - "512", "513", "514", "515", "516", "517", "518", "519", "520", "521", "522", "523", "524", "525", "526", "527", - "528", "529", "530", "531", "532", "533", "534", "535", "536", "537", "538", "539", "540", "541", "542", "543", - "544", "545", "546", "547", "548", "549", "550", "551", "552", "553", "554", "555", "556", "557", "558", "559", - "560", "561", "562", "563", "564", "565", "566", "567", "568", "569", "570", "571", "572", "573", "574", "575", - "576", "577", "578", "579", "580", "581", "582", "583", "584", "585", "586", "587", "588", "589", "590", "591", - "592", "593", "594", "595", "596", "597", "598", "599", "600", "601", "602", "603", "604", "605", "606", "607", - "608", "609", "610", "611", "612", "613", "614", "615", "616", "617", "618", "619", "620", "621", "622", "623", - "624", "625", "626", "627", "628", "629", "630", "631", "632", "633", "634", "635", "636", "637", "638", "639", - "640", "641", "642", "643", "644", "645", "646", "647", "648", "649", "650", "651", "652", "653", "654", "655", - "656", "657", "658", "659", "660", "661", "662", "663", "664", "665", "666", "667", "668", "669", "670", "671", - "672", "673", "674", "675", "676", "677", "678", "679", "680", "681", "682", "683", "684", "685", "686", "687", - "688", "689", "690", "691", "692", "693", "694", "695", "696", "697", "698", "699", "700", "701", "702", "703", - "704", "705", "706", "707", "708", "709", "710", "711", "712", "713", "714", "715", "716", "717", "718", "719", - "720", "721", "722", "723", "724", "725", "726", "727", "728", "729", "730", "731", "732", "733", "734", "735", - "736", "737", "738", "739", "740", "741", "742", "743", "744", "745", "746", "747", "748", "749", "750", "751", - "752", "753", "754", "755", "756", "757", "758", "759", "760", "761", "762", "763", "764", "765", "766", "767", - "768", "769", "770", "771", "772", "773", "774", "775", "776", "777", "778", "779", "780", "781", "782", "783", - "784", "785", "786", "787", "788", "789", "790", "791", "792", "793", "794", "795", "796", "797", "798", "799", - "800", "801", "802", "803", "804", "805", "806", "807", "808", "809", "810", "811", "812", "813", "814", "815", - "816", "817", "818", "819", "820", "821", "822", "823", "824", "825", "826", "827", "828", "829", "830", "831", - "832", "833", "834", "835", "836", "837", "838", "839", "840", "841", "842", "843", "844", "845", "846", "847", - "848", "849", "850", "851", "852", "853", "854", "855", "856", "857", "858", "859", "860", "861", "862", "863", - "864", "865", "866", "867", "868", "869", "870", "871", "872", "873", "874", "875", "876", "877", "878", "879", - "880", "881", "882", "883", "884", "885", "886", "887", "888", "889", "890", "891", "892", "893", "894", "895", - "896", "897", "898", "899", "900", "901", "902", "903", "904", "905", "906", "907", "908", "909", "910", "911", - "912", "913", "914", "915", "916", "917", "918", "919", "920", "921", "922", "923", "924", "925", "926", "927", - "928", "929", "930", "931", "932", "933", "934", "935", "936", "937", "938", "939", "940", "941", "942", "943", - "944", "945", "946", "947", "948", "949", "950", "951", "952", "953", "954", "955", "956", "957", "958", "959", - "960", "961", "962", "963", "964", "965", "966", "967", "968", "969", "970", "971", "972", "973", "974", "975", - "976", "977", "978", "979", "980", "981", "982", "983", "984", "985", "986", "987", "988", "989", "990", "991", - "992", "993", "994", "995", "996", "997", "998", "999"}; - - -/* - *-------------------------------------------------------------------------- - * - * bson_uint32_to_string -- - * - * Converts @value to a string. - * - * If @value is from 0 to 1000, it will use a constant string in the - * data section of the library. - * - * If not, a string will be formatted using @str and snprintf(). This - * is much slower, of course and therefore we try to optimize it out. - * - * @strptr will always be set. It will either point to @str or a - * constant string. You will want to use this as your key. - * - * Parameters: - * @value: A #uint32_t to convert to string. - * @strptr: (out): A pointer to the resulting string. - * @str: (out): Storage for a string made with snprintf. - * @size: Size of @str. - * - * Returns: - * The number of bytes in the resulting string excluding the NULL - * terminator. If the output requires more than @size bytes, then @size - * bytes are written and the result is the number of bytes required - * (excluding the NULL terminator) - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -size_t -bson_uint32_to_string(uint32_t value, /* IN */ - const char **strptr, /* OUT */ - char *str, /* OUT */ - size_t size) /* IN */ -{ - if (value < 1000) { - *strptr = gUint32Strs[value]; - - if (value < 10) { - return 1; - } else if (value < 100) { - return 2; - } else { - return 3; - } - } - - *strptr = str; - - int ret = bson_snprintf(str, size, "%u", value); - // Truncation is OK. - BSON_ASSERT(ret > 0); - BSON_ASSERT(mlib_in_range(size_t, ret)); - return (size_t)ret; -} diff --git a/bsonjs/bson/bson-keys.h b/bsonjs/bson/bson-keys.h deleted file mode 100644 index 2c90b95..0000000 --- a/bsonjs/bson/bson-keys.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_KEYS_H -#define BSON_KEYS_H - - -#include -#include - - -BSON_BEGIN_DECLS - - -BSON_EXPORT(size_t) -bson_uint32_to_string(uint32_t value, const char **strptr, char *str, size_t size); - - -BSON_END_DECLS - - -#endif /* BSON_KEYS_H */ diff --git a/bsonjs/bson/bson-oid.c b/bsonjs/bson/bson-oid.c deleted file mode 100644 index 5aedaf3..0000000 --- a/bsonjs/bson/bson-oid.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - - -/* - * This table contains an array of two character pairs for every possible - * uint8_t. It is used as a lookup table when encoding a bson_oid_t - * to hex formatted ASCII. Performing two characters at a time roughly - * reduces the number of operations by one-half. - */ -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, 12598, 12599, 12600, 12601, 12641, 12642, 12643, 12644, - 12645, 12646, 12848, 12849, 12850, 12851, 12852, 12853, 12854, 12855, 12856, 12857, 12897, 12898, 12899, - 12900, 12901, 12902, 13104, 13105, 13106, 13107, 13108, 13109, 13110, 13111, 13112, 13113, 13153, 13154, - 13155, 13156, 13157, 13158, 13360, 13361, 13362, 13363, 13364, 13365, 13366, 13367, 13368, 13369, 13409, - 13410, 13411, 13412, 13413, 13414, 13616, 13617, 13618, 13619, 13620, 13621, 13622, 13623, 13624, 13625, - 13665, 13666, 13667, 13668, 13669, 13670, 13872, 13873, 13874, 13875, 13876, 13877, 13878, 13879, 13880, - 13881, 13921, 13922, 13923, 13924, 13925, 13926, 14128, 14129, 14130, 14131, 14132, 14133, 14134, 14135, - 14136, 14137, 14177, 14178, 14179, 14180, 14181, 14182, 14384, 14385, 14386, 14387, 14388, 14389, 14390, - 14391, 14392, 14393, 14433, 14434, 14435, 14436, 14437, 14438, 14640, 14641, 14642, 14643, 14644, 14645, - 14646, 14647, 14648, 14649, 14689, 14690, 14691, 14692, 14693, 14694, 24880, 24881, 24882, 24883, 24884, - 24885, 24886, 24887, 24888, 24889, 24929, 24930, 24931, 24932, 24933, 24934, 25136, 25137, 25138, 25139, - 25140, 25141, 25142, 25143, 25144, 25145, 25185, 25186, 25187, 25188, 25189, 25190, 25392, 25393, 25394, - 25395, 25396, 25397, 25398, 25399, 25400, 25401, 25441, 25442, 25443, 25444, 25445, 25446, 25648, 25649, - 25650, 25651, 25652, 25653, 25654, 25655, 25656, 25657, 25697, 25698, 25699, 25700, 25701, 25702, 25904, - 25905, 25906, 25907, 25908, 25909, 25910, 25911, 25912, 25913, 25953, 25954, 25955, 25956, 25957, 25958, - 26160, 26161, 26162, 26163, 26164, 26165, 26166, 26167, 26168, 26169, 26209, 26210, 26211, 26212, 26213, - 26214 -#else - 12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, 24880, 25136, 25392, 25648, 25904, - 26160, 12337, 12593, 12849, 13105, 13361, 13617, 13873, 14129, 14385, 14641, 24881, 25137, 25393, 25649, - 25905, 26161, 12338, 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, 24882, 25138, 25394, - 25650, 25906, 26162, 12339, 12595, 12851, 13107, 13363, 13619, 13875, 14131, 14387, 14643, 24883, 25139, - 25395, 25651, 25907, 26163, 12340, 12596, 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, 24884, - 25140, 25396, 25652, 25908, 26164, 12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, 14389, 14645, - 24885, 25141, 25397, 25653, 25909, 26165, 12342, 12598, 12854, 13110, 13366, 13622, 13878, 14134, 14390, - 14646, 24886, 25142, 25398, 25654, 25910, 26166, 12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, - 14391, 14647, 24887, 25143, 25399, 25655, 25911, 26167, 12344, 12600, 12856, 13112, 13368, 13624, 13880, - 14136, 14392, 14648, 24888, 25144, 25400, 25656, 25912, 26168, 12345, 12601, 12857, 13113, 13369, 13625, - 13881, 14137, 14393, 14649, 24889, 25145, 25401, 25657, 25913, 26169, 12385, 12641, 12897, 13153, 13409, - 13665, 13921, 14177, 14433, 14689, 24929, 25185, 25441, 25697, 25953, 26209, 12386, 12642, 12898, 13154, - 13410, 13666, 13922, 14178, 14434, 14690, 24930, 25186, 25442, 25698, 25954, 26210, 12387, 12643, 12899, - 13155, 13411, 13667, 13923, 14179, 14435, 14691, 24931, 25187, 25443, 25699, 25955, 26211, 12388, 12644, - 12900, 13156, 13412, 13668, 13924, 14180, 14436, 14692, 24932, 25188, 25444, 25700, 25956, 26212, 12389, - 12645, 12901, 13157, 13413, 13669, 13925, 14181, 14437, 14693, 24933, 25189, 25445, 25701, 25957, 26213, - 12390, 12646, 12902, 13158, 13414, 13670, 13926, 14182, 14438, 14694, 24934, 25190, 25446, 25702, 25958, - 26214 -#endif -}; - -void -bson_oid_init(bson_oid_t *oid, /* OUT */ - bson_context_t *context) /* IN */ -{ - BSON_ASSERT(oid); - if (!context) { - context = bson_context_get_default(); - } - - const time_t now = time(NULL); - // Big-endian encode the low 32 bits of the time as the leading 32 bits of the new OID - oid->bytes[0] = (uint8_t)(now >> 24); - oid->bytes[1] = (uint8_t)(now >> 16); - oid->bytes[2] = (uint8_t)(now >> 8); - oid->bytes[3] = (uint8_t)(now >> 0); - // Add randomness - _bson_context_set_oid_rand(context, oid); - _bson_context_set_oid_seq32(context, oid); -} - - -void -bson_oid_init_from_data(bson_oid_t *oid, /* OUT */ - const uint8_t *data) /* IN */ -{ - BSON_ASSERT(oid); - BSON_ASSERT(data); - - memcpy(oid, data, 12); -} - - -void -bson_oid_init_from_string(bson_oid_t *oid, /* OUT */ - const char *str) /* IN */ -{ - BSON_ASSERT(oid); - BSON_ASSERT(str); - - bson_oid_init_from_string_unsafe(oid, str); -} - - -time_t -bson_oid_get_time_t(const bson_oid_t *oid) /* IN */ -{ - BSON_ASSERT(oid); - - return bson_oid_get_time_t_unsafe(oid); -} - - -void -bson_oid_to_string(const bson_oid_t *oid, /* IN */ - char str[BSON_ENSURE_ARRAY_PARAM_SIZE(25)]) /* OUT */ -{ -#if !defined(__i386__) && !defined(__x86_64__) && !defined(_M_IX86) && !defined(_M_X64) - BSON_ASSERT(oid); - BSON_ASSERT(str); - - bson_snprintf(str, - 25, - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - oid->bytes[0], - oid->bytes[1], - oid->bytes[2], - oid->bytes[3], - oid->bytes[4], - oid->bytes[5], - oid->bytes[6], - oid->bytes[7], - oid->bytes[8], - oid->bytes[9], - oid->bytes[10], - oid->bytes[11]); -#else - uint16_t *dst; - uint8_t *id = (uint8_t *)oid; - - BSON_ASSERT(oid); - BSON_ASSERT(str); - - dst = (uint16_t *)(void *)str; - dst[0] = gHexCharPairs[id[0]]; - dst[1] = gHexCharPairs[id[1]]; - dst[2] = gHexCharPairs[id[2]]; - dst[3] = gHexCharPairs[id[3]]; - dst[4] = gHexCharPairs[id[4]]; - dst[5] = gHexCharPairs[id[5]]; - dst[6] = gHexCharPairs[id[6]]; - dst[7] = gHexCharPairs[id[7]]; - dst[8] = gHexCharPairs[id[8]]; - dst[9] = gHexCharPairs[id[9]]; - dst[10] = gHexCharPairs[id[10]]; - dst[11] = gHexCharPairs[id[11]]; - str[24] = '\0'; -#endif -} - - -uint32_t -bson_oid_hash(const bson_oid_t *oid) /* IN */ -{ - BSON_ASSERT(oid); - - return bson_oid_hash_unsafe(oid); -} - - -int -bson_oid_compare(const bson_oid_t *oid1, /* IN */ - const bson_oid_t *oid2) /* IN */ -{ - BSON_ASSERT(oid1); - BSON_ASSERT(oid2); - - return bson_oid_compare_unsafe(oid1, oid2); -} - - -bool -bson_oid_equal(const bson_oid_t *oid1, /* IN */ - const bson_oid_t *oid2) /* IN */ -{ - BSON_ASSERT(oid1); - BSON_ASSERT(oid2); - - return bson_oid_equal_unsafe(oid1, oid2); -} - - -void -bson_oid_copy(const bson_oid_t *src, /* IN */ - bson_oid_t *dst) /* OUT */ -{ - BSON_ASSERT(src); - BSON_ASSERT(dst); - - bson_oid_copy_unsafe(src, dst); -} - - -bool -bson_oid_is_valid(const char *str, /* IN */ - size_t length) /* IN */ -{ - size_t i; - - BSON_ASSERT(str); - - if ((length == 25) && (str[24] == '\0')) { - length = 24; - } - - if (length == 24) { - for (i = 0; i < length; i++) { - switch (str[i]) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - break; - default: - return false; - } - } - return true; - } - - return false; -} diff --git a/bsonjs/bson/bson-oid.h b/bsonjs/bson/bson-oid.h deleted file mode 100644 index d63aa07..0000000 --- a/bsonjs/bson/bson-oid.h +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_OID_H -#define BSON_OID_H - - -#include -#include -#include -#include - -#include - - -BSON_BEGIN_DECLS - - -BSON_EXPORT(int) -bson_oid_compare(const bson_oid_t *oid1, const bson_oid_t *oid2); -BSON_EXPORT(void) -bson_oid_copy(const bson_oid_t *src, bson_oid_t *dst); -BSON_EXPORT(bool) -bson_oid_equal(const bson_oid_t *oid1, const bson_oid_t *oid2); -BSON_EXPORT(bool) -bson_oid_is_valid(const char *str, size_t length); -BSON_EXPORT(time_t) -bson_oid_get_time_t(const bson_oid_t *oid); -BSON_EXPORT(uint32_t) -bson_oid_hash(const bson_oid_t *oid); -BSON_EXPORT(void) -bson_oid_init(bson_oid_t *oid, bson_context_t *context); -BSON_EXPORT(void) -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_to_string(const bson_oid_t *oid, char str[25]); - - -/** - * bson_oid_compare_unsafe: - * @oid1: A bson_oid_t. - * @oid2: A bson_oid_t. - * - * Performs a qsort() style comparison between @oid1 and @oid2. - * - * This function is meant to be as fast as possible and therefore performs - * no argument validation. That is the callers responsibility. - * - * Returns: An integer < 0 if @oid1 is less than @oid2. Zero if they are equal. - * An integer > 0 if @oid1 is greater than @oid2. - */ -static BSON_INLINE int -bson_oid_compare_unsafe(const bson_oid_t *oid1, const bson_oid_t *oid2) -{ - return memcmp(oid1, oid2, sizeof *oid1); -} - - -/** - * bson_oid_equal_unsafe: - * @oid1: A bson_oid_t. - * @oid2: A bson_oid_t. - * - * Checks the equality of @oid1 and @oid2. - * - * This function is meant to be as fast as possible and therefore performs - * no checks for argument validity. That is the callers responsibility. - * - * Returns: true if @oid1 and @oid2 are equal; otherwise false. - */ -static BSON_INLINE bool -bson_oid_equal_unsafe(const bson_oid_t *oid1, const bson_oid_t *oid2) -{ - return !memcmp(oid1, oid2, sizeof *oid1); -} - -/** - * bson_oid_hash_unsafe: - * @oid: A bson_oid_t. - * - * This function performs a DJB style hash upon the bytes contained in @oid. - * The result is a hash key suitable for use in a hashtable. - * - * This function is meant to be as fast as possible and therefore performs no - * validation of arguments. The caller is responsible to ensure they are - * passing valid arguments. - * - * Returns: A uint32_t containing a hash code. - */ -static BSON_INLINE uint32_t -bson_oid_hash_unsafe(const bson_oid_t *oid) -{ - uint32_t hash = 5381; - uint32_t i; - - for (i = 0; i < sizeof oid->bytes; i++) { - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - hash = ((hash << 5) + hash) + oid->bytes[i]; - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - } - - return hash; -} - - -/** - * bson_oid_copy_unsafe: - * @src: A bson_oid_t to copy from. - * @dst: A bson_oid_t to copy into. - * - * Copies the contents of @src into @dst. This function is meant to be as - * fast as possible and therefore performs no argument checking. It is the - * callers responsibility to ensure they are passing valid data into the - * function. - */ -static BSON_INLINE void -bson_oid_copy_unsafe(const bson_oid_t *src, bson_oid_t *dst) -{ - memcpy(dst, src, sizeof *src); -} - - -/** - * bson_oid_parse_hex_char: - * @hex: A character to parse to its integer value. - * - * This function contains a jump table to return the integer value for a - * character containing a hexadecimal value (0-9, a-f, A-F). If the character - * is not a hexadecimal character then zero is returned. - * - * Returns: An integer between 0 and 15. - */ -static BSON_INLINE uint8_t -bson_oid_parse_hex_char(char hex) -{ - switch (hex) { - case '0': - return 0; - case '1': - return 1; - case '2': - return 2; - case '3': - return 3; - case '4': - return 4; - case '5': - return 5; - case '6': - return 6; - case '7': - return 7; - case '8': - return 8; - case '9': - return 9; - case 'a': - case 'A': - return 0xa; - case 'b': - case 'B': - return 0xb; - case 'c': - case 'C': - return 0xc; - case 'd': - case 'D': - return 0xd; - case 'e': - case 'E': - return 0xe; - case 'f': - case 'F': - return 0xf; - default: - return 0; - } -} - - -/** - * bson_oid_init_from_string_unsafe: - * @oid: A bson_oid_t to store the result. - * @str: A 24-character hexadecimal encoded string. - * - * Parses a string containing 24 hexadecimal encoded bytes into a bson_oid_t. - * This function is meant to be as fast as possible and inlined into your - * code. For that purpose, the function does not perform any sort of bounds - * checking and it is the callers responsibility to ensure they are passing - * valid input to the function. - */ -static BSON_INLINE void -bson_oid_init_from_string_unsafe(bson_oid_t *oid, const char *str) -{ - int i; - - for (i = 0; i < 12; i++) { - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - oid->bytes[i] = (uint8_t)((bson_oid_parse_hex_char(str[2 * i]) << 4) | (bson_oid_parse_hex_char(str[2 * i + 1]))); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - } -} - - -/** - * bson_oid_get_time_t_unsafe: - * @oid: A bson_oid_t. - * - * Fetches the time @oid was generated. - * - * Returns: A time_t containing the UNIX timestamp of generation. - */ -static BSON_INLINE time_t -bson_oid_get_time_t_unsafe(const bson_oid_t *oid) -{ - uint32_t t; - - memcpy(&t, oid, sizeof(t)); - return BSON_UINT32_FROM_BE(t); -} - - -BSON_END_DECLS - - -#endif /* BSON_OID_H */ diff --git a/bsonjs/bson/bson-prelude.h b/bsonjs/bson/bson-prelude.h deleted file mode 100644 index adbbf14..0000000 --- a/bsonjs/bson/bson-prelude.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if !defined(BSON_INSIDE) && !defined(BSON_COMPILATION) -#error "Only can be included directly." -#endif diff --git a/bsonjs/bson/bson-reader.c b/bsonjs/bson/bson-reader.c deleted file mode 100644 index bd26930..0000000 --- a/bsonjs/bson/bson-reader.c +++ /dev/null @@ -1,817 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include - -#include - -#include -#ifdef BSON_OS_WIN32 -#include -#include -#endif -#include -#include - -#include -#include - -#include -#include - - -typedef enum { - BSON_READER_HANDLE = 1, - BSON_READER_DATA = 2, -} bson_reader_type_t; - - -typedef struct { - bson_reader_type_t type; - void *handle; - bool done : 1; - bool failed : 1; - size_t end; - size_t len; - size_t offset; - size_t bytes_read; - bson_t inline_bson; - uint8_t *data; - bson_reader_read_func_t read_func; - bson_reader_destroy_func_t destroy_func; -} bson_reader_handle_t; - - -typedef struct { - int fd; - bool do_close; -} bson_reader_handle_fd_t; - - -typedef struct { - bson_reader_type_t type; - const uint8_t *data; - size_t length; - size_t offset; - bson_t inline_bson; -} bson_reader_data_t; - - -/* - *-------------------------------------------------------------------------- - * - * _bson_reader_handle_fill_buffer -- - * - * Attempt to read as much as possible until the underlying buffer - * in @reader is filled or we have reached end-of-stream or - * read failure. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static void -_bson_reader_handle_fill_buffer(bson_reader_handle_t *reader) /* IN */ -{ - ssize_t ret; - - /* - * Handle first read specially. - */ - if ((!reader->done) && (!reader->offset) && (!reader->end)) { - ret = reader->read_func(reader->handle, &reader->data[0], reader->len); - - if (ret <= 0) { - reader->done = true; - return; - } - reader->bytes_read += ret; - - reader->end = ret; - return; - } - - /* - * Move valid data to head. - */ - memmove(&reader->data[0], &reader->data[reader->offset], reader->end - reader->offset); - reader->end = reader->end - reader->offset; - reader->offset = 0; - - /* - * Read in data to fill the buffer. - */ - ret = reader->read_func(reader->handle, &reader->data[reader->end], reader->len - reader->end); - - if (ret <= 0) { - reader->done = true; - reader->failed = (ret < 0); - } else { - reader->bytes_read += ret; - reader->end += ret; - } - - BSON_ASSERT(reader->offset == 0); - BSON_ASSERT(reader->end <= reader->len); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_reader_new_from_handle -- - * - * Allocates and initializes a new bson_reader_t using the opaque - * handle provided. - * - * Parameters: - * @handle: an opaque handle to use to read data. - * @rf: a function to perform reads on @handle. - * @df: a function to release @handle, or NULL. - * - * Returns: - * A newly allocated bson_reader_t if successful, otherwise NULL. - * Free the successful result with bson_reader_destroy(). - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bson_reader_t * -bson_reader_new_from_handle(void *handle, bson_reader_read_func_t rf, bson_reader_destroy_func_t df) -{ - bson_reader_handle_t *real; - - BSON_ASSERT(handle); - BSON_ASSERT(rf); - - real = BSON_ALIGNED_ALLOC0(bson_reader_handle_t); - real->type = BSON_READER_HANDLE; - real->data = bson_malloc0(1024); - real->handle = handle; - real->len = 1024; - real->offset = 0; - - bson_reader_set_read_func((bson_reader_t *)real, rf); - - if (df) { - bson_reader_set_destroy_func((bson_reader_t *)real, df); - } - - _bson_reader_handle_fill_buffer(real); - - return (bson_reader_t *)real; -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_reader_handle_fd_destroy -- - * - * Cleanup allocations associated with state created in - * bson_reader_new_from_fd(). - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static void -_bson_reader_handle_fd_destroy(void *handle) /* IN */ -{ - bson_reader_handle_fd_t *fd = handle; - - if (fd) { - if ((fd->fd != -1) && fd->do_close) { -#ifdef _WIN32 - _close(fd->fd); -#else - close(fd->fd); -#endif - } - bson_free(fd); - } -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_reader_handle_fd_read -- - * - * Perform read on opaque handle created in - * bson_reader_new_from_fd(). - * - * The underlying file descriptor is read from the current position - * using the bson_reader_handle_fd_t allocated. - * - * Returns: - * -1 on failure. - * 0 on end of stream. - * Greater than zero on success. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static ssize_t -_bson_reader_handle_fd_read(void *handle, /* IN */ - void *buf, /* IN */ - size_t len) /* IN */ -{ - bson_reader_handle_fd_t *fd = handle; - ssize_t ret = -1; - - if (fd && (fd->fd != -1)) { - again: -#ifdef BSON_OS_WIN32 - ret = _read(fd->fd, buf, (unsigned int)len); -#else - ret = read(fd->fd, buf, len); -#endif - if ((ret == -1) && (errno == EAGAIN)) { - goto again; - } - } - - return ret; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_reader_new_from_fd -- - * - * Create a new bson_reader_t using the file-descriptor provided. - * - * Parameters: - * @fd: a libc style file-descriptor. - * @close_on_destroy: if close() should be called on @fd when - * bson_reader_destroy() is called. - * - * Returns: - * A newly allocated bson_reader_t on success; otherwise NULL. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bson_reader_t * -bson_reader_new_from_fd(int fd, /* IN */ - bool close_on_destroy) /* IN */ -{ - bson_reader_handle_fd_t *handle; - - BSON_ASSERT(fd != -1); - - handle = bson_malloc0(sizeof *handle); - handle->fd = fd; - handle->do_close = close_on_destroy; - - return bson_reader_new_from_handle(handle, _bson_reader_handle_fd_read, _bson_reader_handle_fd_destroy); -} - - -/** - * bson_reader_set_read_func: - * @reader: A bson_reader_t. - * - * Note that @reader must be initialized by bson_reader_init_from_handle(), or - * data - * will be destroyed. - */ -/* - *-------------------------------------------------------------------------- - * - * bson_reader_set_read_func -- - * - * Set the read func to be provided for @reader. - * - * You probably want to use bson_reader_new_from_handle() or - * bson_reader_new_from_fd() instead. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_reader_set_read_func(bson_reader_t *reader, /* IN */ - bson_reader_read_func_t func) /* IN */ -{ - bson_reader_handle_t *real = (bson_reader_handle_t *)reader; - - BSON_ASSERT(reader->type == BSON_READER_HANDLE); - - real->read_func = func; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_reader_set_destroy_func -- - * - * Set the function to cleanup state when @reader is destroyed. - * - * You probably want bson_reader_new_from_fd() or - * bson_reader_new_from_handle() instead. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_reader_set_destroy_func(bson_reader_t *reader, /* IN */ - bson_reader_destroy_func_t func) /* IN */ -{ - bson_reader_handle_t *real = (bson_reader_handle_t *)reader; - - BSON_ASSERT(reader->type == BSON_READER_HANDLE); - - real->destroy_func = func; -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_reader_handle_grow_buffer -- - * - * Grow the buffer to the next power of two. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static void -_bson_reader_handle_grow_buffer(bson_reader_handle_t *reader) /* IN */ -{ - size_t size; - - size = reader->len * 2; - reader->data = bson_realloc(reader->data, size); - reader->len = size; -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_reader_handle_tell -- - * - * Tell the current position within the underlying file-descriptor. - * - * Returns: - * An off_t containing the current offset. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static off_t -_bson_reader_handle_tell(bson_reader_handle_t *reader) /* IN */ -{ - off_t off; - - off = (off_t)reader->bytes_read; - off -= (off_t)reader->end; - off += (off_t)reader->offset; - - return off; -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_reader_handle_read -- - * - * Read the next chunk of data from the underlying file descriptor - * and return a bson_t which should not be modified. - * - * There was a failure if NULL is returned and @reached_eof is - * not set to true. - * - * Returns: - * NULL on failure or end of stream. - * - * Side effects: - * @reached_eof is set if non-NULL. - * - *-------------------------------------------------------------------------- - */ - -static const bson_t * -_bson_reader_handle_read(bson_reader_handle_t *reader, /* IN */ - bool *reached_eof) /* IN */ -{ - if (reached_eof) { - *reached_eof = false; - } - - while (!reader->done) { - if ((reader->end - reader->offset) < 4) { - _bson_reader_handle_fill_buffer(reader); - continue; - } - - const int32_t blen = mlib_read_i32le(reader->data + reader->offset); - if (blen < 5) { - return NULL; - } - - if (blen > (int32_t)(reader->end - reader->offset)) { - if (blen > (int32_t)reader->len) { - _bson_reader_handle_grow_buffer(reader); - } - - _bson_reader_handle_fill_buffer(reader); - continue; - } - - if (!bson_init_static(&reader->inline_bson, &reader->data[reader->offset], (uint32_t)blen)) { - return NULL; - } - - reader->offset += blen; - - return &reader->inline_bson; - } - - if (reached_eof) { - *reached_eof = reader->done && !reader->failed; - } - - return NULL; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_reader_new_from_data -- - * - * Allocates and initializes a new bson_reader_t that reads the memory - * provided as a stream of BSON documents. - * - * Parameters: - * @data: A buffer to read BSON documents from. - * @length: The length of @data. - * - * Returns: - * A newly allocated bson_reader_t that should be freed with - * bson_reader_destroy(). - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bson_reader_t * -bson_reader_new_from_data(const uint8_t *data, /* IN */ - size_t length) /* IN */ -{ - bson_reader_data_t *real; - - BSON_ASSERT(data); - - real = BSON_ALIGNED_ALLOC0(bson_reader_data_t); - real->type = BSON_READER_DATA; - real->data = data; - real->length = length; - real->offset = 0; - - return (bson_reader_t *)real; -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_reader_data_read -- - * - * Read the next document from the underlying buffer. - * - * Returns: - * NULL on failure or end of stream. - * a bson_t which should not be modified. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static const bson_t * -_bson_reader_data_read(bson_reader_data_t *reader, /* IN */ - bool *reached_eof) /* IN */ -{ - if (reached_eof) { - *reached_eof = false; - } - - if ((reader->offset + 4) < reader->length) { - const int32_t blen = mlib_read_i32le(reader->data + reader->offset); - if (blen < 5) { - return NULL; - } - - if (blen > (int32_t)(reader->length - reader->offset)) { - return NULL; - } - - if (!bson_init_static(&reader->inline_bson, &reader->data[reader->offset], (uint32_t)blen)) { - return NULL; - } - - reader->offset += blen; - - return &reader->inline_bson; - } - - if (reached_eof) { - *reached_eof = (reader->offset == reader->length); - } - - return NULL; -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_reader_data_tell -- - * - * Tell the current position in the underlying buffer. - * - * Returns: - * An off_t of the current offset. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static off_t -_bson_reader_data_tell(bson_reader_data_t *reader) /* IN */ -{ - return (off_t)reader->offset; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_reader_destroy -- - * - * Release a bson_reader_t created with bson_reader_new_from_data(), - * bson_reader_new_from_fd(), or bson_reader_new_from_handle(). - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_reader_destroy(bson_reader_t *reader) /* IN */ -{ - if (!reader) { - return; - } - - switch (reader->type) { - case 0: - break; - case BSON_READER_HANDLE: { - bson_reader_handle_t *handle = (bson_reader_handle_t *)reader; - - if (handle->destroy_func) { - handle->destroy_func(handle->handle); - } - - bson_free(handle->data); - } break; - case BSON_READER_DATA: - break; - default: - fprintf(stderr, "No such reader type: %02x\n", reader->type); - break; - } - - reader->type = 0; - - bson_free(reader); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_reader_read -- - * - * Reads the next bson_t in the underlying memory or storage. The - * resulting bson_t should not be modified or freed. You may copy it - * and iterate over it. Functions that take a const bson_t* are safe - * to use. - * - * This structure does not survive calls to bson_reader_read() or - * bson_reader_destroy() as it uses memory allocated by the reader or - * underlying storage/memory. - * - * If NULL is returned then @reached_eof will be set to true if the - * end of the file or buffer was reached. This indicates if there was - * an error parsing the document stream. - * - * Returns: - * A const bson_t that should not be modified or freed. - * NULL on failure or end of stream. - * - * Side effects: - * @reached_eof is set if non-NULL. - * - *-------------------------------------------------------------------------- - */ - -const bson_t * -bson_reader_read(bson_reader_t *reader, /* IN */ - bool *reached_eof) /* OUT */ -{ - BSON_ASSERT(reader); - - switch (reader->type) { - case BSON_READER_HANDLE: - return _bson_reader_handle_read((bson_reader_handle_t *)reader, reached_eof); - - case BSON_READER_DATA: - return _bson_reader_data_read((bson_reader_data_t *)reader, reached_eof); - - default: - fprintf(stderr, "No such reader type: %02x\n", reader->type); - break; - } - - return NULL; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_reader_tell -- - * - * Return the current position in the underlying reader. This will - * always be at the beginning of a bson document or end of file. - * - * Returns: - * An off_t containing the current offset. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -off_t -bson_reader_tell(bson_reader_t *reader) /* IN */ -{ - BSON_ASSERT(reader); - - switch (reader->type) { - case BSON_READER_HANDLE: - return _bson_reader_handle_tell((bson_reader_handle_t *)reader); - - case BSON_READER_DATA: - return _bson_reader_data_tell((bson_reader_data_t *)reader); - - default: - fprintf(stderr, "No such reader type: %02x\n", reader->type); - return -1; - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_reader_new_from_file -- - * - * A convenience function to open a file containing sequential - * bson documents and read them using bson_reader_t. - * - * Returns: - * A new bson_reader_t if successful, otherwise NULL and - * @error is set. Free the non-NULL result with - * bson_reader_destroy(). - * - * Side effects: - * @error may be set. - * - *-------------------------------------------------------------------------- - */ - -bson_reader_t * -bson_reader_new_from_file(const char *path, /* IN */ - bson_error_t *error) /* OUT */ -{ - char errmsg_buf[BSON_ERROR_BUFFER_SIZE]; - char *errmsg; - int fd; - - BSON_ASSERT(path); - -#ifdef BSON_OS_WIN32 - if (_sopen_s(&fd, path, (_O_RDONLY | _O_BINARY), _SH_DENYNO, 0) != 0) { - fd = -1; - } -#else - fd = open(path, O_RDONLY); -#endif - - if (fd == -1) { - errmsg = bson_strerror_r(errno, errmsg_buf, sizeof errmsg_buf); - bson_set_error(error, BSON_ERROR_READER, BSON_ERROR_READER_BADFD, "%s", errmsg); - return NULL; - } - - return bson_reader_new_from_fd(fd, true); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_reader_reset -- - * - * Restore the reader to its initial state. Valid only for readers - * created with bson_reader_new_from_data. - * - *-------------------------------------------------------------------------- - */ - -void -bson_reader_reset(bson_reader_t *reader) -{ - bson_reader_data_t *real = (bson_reader_data_t *)reader; - - if (real->type != BSON_READER_DATA) { - fprintf(stderr, "Reader type cannot be reset\n"); - return; - } - - real->offset = 0; -} diff --git a/bsonjs/bson/bson-reader.h b/bsonjs/bson/bson-reader.h deleted file mode 100644 index c557573..0000000 --- a/bsonjs/bson/bson-reader.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_READER_H -#define BSON_READER_H - - -#include -#include -#include - - -BSON_BEGIN_DECLS - - -#define BSON_ERROR_READER_BADFD 1 - - -/* - *-------------------------------------------------------------------------- - * - * bson_reader_read_func_t -- - * - * This function is a callback used by bson_reader_t to read the - * next chunk of data from the underlying opaque file descriptor. - * - * This function is meant to operate similar to the read() function - * as part of libc on UNIX-like systems. - * - * Parameters: - * @handle: The handle to read from. - * @buf: The buffer to read into. - * @count: The number of bytes to read. - * - * Returns: - * 0 for end of stream. - * -1 for read failure. - * Greater than zero for number of bytes read into @buf. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -typedef ssize_t(BSON_CALL *bson_reader_read_func_t)(void *handle, /* IN */ - void *buf, /* IN */ - size_t count); /* IN */ - - -/* - *-------------------------------------------------------------------------- - * - * bson_reader_destroy_func_t -- - * - * Destroy callback to release any resources associated with the - * opaque handle. - * - * Parameters: - * @handle: the handle provided to bson_reader_new_from_handle(). - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -typedef void(BSON_CALL *bson_reader_destroy_func_t)(void *handle); /* IN */ - - -BSON_EXPORT(bson_reader_t *) -bson_reader_new_from_handle(void *handle, bson_reader_read_func_t rf, bson_reader_destroy_func_t df); -BSON_EXPORT(bson_reader_t *) -bson_reader_new_from_fd(int fd, bool close_on_destroy); -BSON_EXPORT(bson_reader_t *) -bson_reader_new_from_file(const char *path, bson_error_t *error); -BSON_EXPORT(bson_reader_t *) -bson_reader_new_from_data(const uint8_t *data, size_t length); -BSON_EXPORT(void) -bson_reader_destroy(bson_reader_t *reader); -BSON_EXPORT(void) -bson_reader_set_read_func(bson_reader_t *reader, bson_reader_read_func_t func); -BSON_EXPORT(void) -bson_reader_set_destroy_func(bson_reader_t *reader, bson_reader_destroy_func_t func); -BSON_EXPORT(const bson_t *) -bson_reader_read(bson_reader_t *reader, bool *reached_eof); -BSON_EXPORT(off_t) -bson_reader_tell(bson_reader_t *reader); -BSON_EXPORT(void) -bson_reader_reset(bson_reader_t *reader); - -BSON_END_DECLS - - -#endif /* BSON_READER_H */ diff --git a/bsonjs/bson/bson-string.c b/bsonjs/bson/bson-string.c deleted file mode 100644 index 7d4a1db..0000000 --- a/bsonjs/bson/bson-string.c +++ /dev/null @@ -1,555 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#ifdef BSON_HAVE_STRINGS_H -#include -#else -#include -#endif - - -/* - *-------------------------------------------------------------------------- - * - * bson_strdup -- - * - * Portable strdup(). - * - * Returns: - * A newly allocated string that should be freed with bson_free(). - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -char * -bson_strdup(const char *str) /* IN */ -{ - long len; - char *out; - - if (!str) { - return NULL; - } - - len = (long)strlen(str); - out = bson_malloc(len + 1); - - if (!out) { - return NULL; - } - - memcpy(out, str, len + 1); - - return out; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_strdupv_printf -- - * - * Like bson_strdup_printf() but takes a va_list. - * - * Returns: - * A newly allocated string that should be freed with bson_free(). - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -char * -bson_strdupv_printf(const char *format, /* IN */ - va_list args) /* IN */ -{ - va_list my_args; - char *buf; - int len = 32; - int n; - - BSON_ASSERT(format); - - buf = bson_malloc0(len); - - while (true) { - va_copy(my_args, args); - n = bson_vsnprintf(buf, len, format, my_args); - va_end(my_args); - - if (n > -1 && n < len) { - return buf; - } - - if (n > -1) { - len = n + 1; - } else { - len *= 2; - } - - buf = bson_realloc(buf, len); - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_strdup_printf -- - * - * Convenience function that formats a string according to @format - * and returns a copy of it. - * - * Returns: - * A newly created string that should be freed with bson_free(). - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -char * -bson_strdup_printf(const char *format, /* IN */ - ...) /* IN */ -{ - va_list args; - char *ret; - - BSON_ASSERT(format); - - va_start(args, format); - ret = bson_strdupv_printf(format, args); - va_end(args); - - return ret; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_strndup -- - * - * A portable strndup(). - * - * Returns: - * A newly allocated string that should be freed with bson_free(). - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -char * -bson_strndup(const char *str, /* IN */ - size_t n_bytes) /* IN */ -{ - char *ret; - - BSON_ASSERT(str); - - ret = bson_malloc(n_bytes + 1); - bson_strncpy(ret, str, n_bytes + 1); - - return ret; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_strfreev -- - * - * Frees each string in a NULL terminated array of strings. - * This also frees the underlying array. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_strfreev(char **str) /* IN */ -{ - if (str) { - for (char **ptr = str; *ptr != NULL; ++ptr) { - bson_free(*ptr); - } - - bson_free(str); - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_strnlen -- - * - * A portable strnlen(). - * - * Returns: - * The length of @s up to @maxlen. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -size_t -bson_strnlen(const char *s, /* IN */ - size_t maxlen) /* IN */ -{ -#ifdef BSON_HAVE_STRNLEN - return strnlen(s, maxlen); -#else - size_t i; - - for (i = 0; i < maxlen; i++) { - if (s[i] == '\0') { - return i; - } - } - - return maxlen; -#endif -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_strncpy -- - * - * A portable strncpy. - * - * Copies @src into @dst, which must be @size bytes or larger. - * The result is guaranteed to be \0 terminated. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_strncpy(char *dst, /* IN */ - const char *src, /* IN */ - size_t size) /* IN */ -{ - if (size == 0) { - return; - } - -/* Prefer strncpy_s for MSVC, or strlcpy, which has additional checks and only - * adds one trailing \0 */ -#ifdef _MSC_VER - strncpy_s(dst, size, src, _TRUNCATE); -#elif defined(BSON_HAVE_STRLCPY) - strlcpy(dst, src, size); -#else - strncpy(dst, src, size); - dst[size - 1] = '\0'; -#endif -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_vsnprintf -- - * - * A portable vsnprintf. - * - * If more than @size bytes are required (exluding the null byte), - * then @size bytes will be written to @string and the return value - * is the number of bytes required. - * - * This function will always return a NULL terminated string. - * - * Returns: - * The number of bytes required for @format excluding the null byte. - * - * Side effects: - * @str is initialized with the formatted string. - * - *-------------------------------------------------------------------------- - */ - -int -bson_vsnprintf(char *str, /* IN */ - size_t size, /* IN */ - const char *format, /* IN */ - va_list ap) /* IN */ -{ -#ifdef _MSC_VER - int r = -1; - - BSON_ASSERT(str); - - if (size == 0) { - return 0; - } - - r = _vsnprintf_s(str, size, _TRUNCATE, format, ap); - if (r == -1) { - r = _vscprintf(format, ap); - } - - str[size - 1] = '\0'; - - return r; -#else - int r; - - BSON_ASSERT(str); - - if (size == 0) { - return 0; - } - - r = vsnprintf(str, size, format, ap); - str[size - 1] = '\0'; - return r; -#endif -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_snprintf -- - * - * A portable snprintf. - * - * If @format requires more than @size bytes, then @size bytes are - * written and the result is the number of bytes required (excluding - * the null byte). - * - * This function will always return a NULL terminated string. - * - * Returns: - * The number of bytes required for @format. - * - * Side effects: - * @str is initialized. - * - *-------------------------------------------------------------------------- - */ - -int -bson_snprintf(char *str, /* IN */ - size_t size, /* IN */ - const char *format, /* IN */ - ...) -{ - int r; - va_list ap; - - BSON_ASSERT(str); - - va_start(ap, format); - r = bson_vsnprintf(str, size, format, ap); - va_end(ap); - - return r; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_ascii_strtoll -- - * - * A portable strtoll. - * - * Convert a string to a 64-bit signed integer according to the given - * @base, which must be 16, 10, or 8. Leading whitespace will be ignored. - * - * If base is 0 is passed in, the base is inferred from the string's - * leading characters. Base-16 numbers start with "0x" or "0X", base-8 - * numbers start with "0", base-10 numbers start with a digit from 1 to 9. - * - * If @e is not NULL, it will be assigned the address of the first invalid - * character of @s, or its null terminating byte if the entire string was - * valid. - * - * If an invalid value is encountered, errno will be set to EINVAL and - * zero will be returned. If the number is out of range, errno is set to - * ERANGE and LLONG_MAX or LLONG_MIN is returned. - * - * Returns: - * The result of the conversion. - * - * Side effects: - * errno will be set on error. - * - *-------------------------------------------------------------------------- - */ - -int64_t -bson_ascii_strtoll(const char *s, char **e, int base) -{ - char *tok = (char *)s; - char *digits_start; - char c; - int64_t number = 0; - int64_t sign = 1; - int64_t cutoff; - int64_t cutlim; - - errno = 0; - - if (!s) { - errno = EINVAL; - return 0; - } - - c = *tok; - - while (bson_isspace(c)) { - c = *++tok; - } - - if (c == '-') { - sign = -1; - c = *++tok; - } else if (c == '+') { - c = *++tok; - } else if (!isdigit(c)) { - errno = EINVAL; - return 0; - } - - /* from here down, inspired by NetBSD's strtoll */ - if ((base == 0 || base == 16) && c == '0' && (tok[1] == 'x' || tok[1] == 'X')) { - tok += 2; - c = *tok; - base = 16; - } - - if (base == 0) { - base = c == '0' ? 8 : 10; - } - - /* Cutoff is the greatest magnitude we'll be able to multiply by base without - * range error. If the current number is past cutoff and we see valid digit, - * fail. If the number is *equal* to cutoff, then the next digit must be less - * than cutlim, otherwise fail. - */ - cutoff = sign == -1 ? INT64_MIN : INT64_MAX; - cutlim = (int)(cutoff % base); - cutoff /= base; - if (sign == -1) { - if (cutlim > 0) { - cutlim -= base; - cutoff += 1; - } - cutlim = -cutlim; - } - - digits_start = tok; - - while ((c = *tok)) { - if (isdigit(c)) { - c -= '0'; - } else if (isalpha(c)) { - c -= isupper(c) ? 'A' - 10 : 'a' - 10; - } else { - /* end of number string */ - break; - } - - if (c >= base) { - break; - } - - if (sign == -1) { - if (number < cutoff || (number == cutoff && c > cutlim)) { - number = INT64_MIN; - errno = ERANGE; - break; - } else { - number *= base; - number -= c; - } - } else { - if (number > cutoff || (number == cutoff && c > cutlim)) { - number = INT64_MAX; - errno = ERANGE; - break; - } else { - number *= base; - number += c; - } - } - - tok++; - } - - /* did we parse any digits at all? */ - if (e != NULL && tok > digits_start) { - *e = tok; - } - - return number; -} - - -int -bson_strcasecmp(const char *s1, const char *s2) -{ -#ifdef BSON_OS_WIN32 - return _stricmp(s1, s2); -#else - return strcasecmp(s1, s2); -#endif -} - - -bool -bson_isspace(int c) -{ - return c >= -1 && c <= 255 && isspace(c); -} diff --git a/bsonjs/bson/bson-string.h b/bsonjs/bson/bson-string.h deleted file mode 100644 index ab95600..0000000 --- a/bsonjs/bson/bson-string.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_STRING_H -#define BSON_STRING_H - - -#include -#include - -#include - - -BSON_BEGIN_DECLS - -BSON_EXPORT(char *) -bson_strdup(const char *str); - -BSON_EXPORT(char *) -bson_strdup_printf(const char *format, ...) BSON_GNUC_PRINTF(1, 2); - -BSON_EXPORT(char *) -bson_strdupv_printf(const char *format, va_list args) BSON_GNUC_PRINTF(1, 0); - -BSON_EXPORT(char *) -bson_strndup(const char *str, size_t n_bytes); - -BSON_EXPORT(void) -bson_strncpy(char *dst, const char *src, size_t size); - -BSON_EXPORT(int) -bson_vsnprintf(char *str, size_t size, const char *format, va_list ap) BSON_GNUC_PRINTF(3, 0); - -BSON_EXPORT(int) -bson_snprintf(char *str, size_t size, const char *format, ...) BSON_GNUC_PRINTF(3, 4); - -BSON_EXPORT(void) -bson_strfreev(char **strv); - -BSON_EXPORT(size_t) -bson_strnlen(const char *s, size_t maxlen); - -BSON_EXPORT(int64_t) -bson_ascii_strtoll(const char *str, char **endptr, int base); - -BSON_EXPORT(int) -bson_strcasecmp(const char *s1, const char *s2); - -BSON_EXPORT(bool) -bson_isspace(int c); - - -BSON_END_DECLS - - -#endif /* BSON_STRING_H */ diff --git a/bsonjs/bson/bson-timegm-private.h b/bsonjs/bson/bson-timegm-private.h deleted file mode 100644 index e4f277c..0000000 --- a/bsonjs/bson/bson-timegm-private.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_TIMEGM_PRIVATE_H -#define BSON_TIMEGM_PRIVATE_H - - -#include -#include - - -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 */ - const char *tm_zone; /* timezone abbreviation */ -}; - -int64_t -_bson_timegm(struct bson_tm *const tmp); - -BSON_END_DECLS - - -#endif /* BSON_TIMEGM_PRIVATE_H */ diff --git a/bsonjs/bson/bson-timegm.c b/bsonjs/bson/bson-timegm.c deleted file mode 100644 index c289f63..0000000 --- a/bsonjs/bson/bson-timegm.c +++ /dev/null @@ -1,668 +0,0 @@ -/* -** The original version of this file is in the public domain, so clarified as of -** 1996-06-05 by Arthur David Olson. -*/ - -/* -** Leap second handling from Bradley White. -** POSIX-style TZ environment variable handling from Guy Harris. -** Updated to use int64_t's instead of system-dependent definitions of int64_t -** and struct tm by A. Jesse Jiryu Davis for MongoDB, Inc. -*/ - -#include - -#include -#include - -#include -#include /* for INT64_MAX and INT64_MIN */ -#include - -#if 2 < __GNUC__ + (96 <= __GNUC_MINOR__) -#define ATTRIBUTE_PURE __attribute__((__pure__)) -#else -#define ATTRIBUTE_PURE /* empty */ -#endif - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunknown-pragmas" -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wshift-negative-value" -#endif - -/* The minimum and maximum finite time values. */ -static int64_t const time_t_min = INT64_MIN; -static int64_t const time_t_max = INT64_MAX; - -#ifdef __clang__ -#pragma clang diagnostic pop -#pragma clang diagnostic pop -#endif - -#ifndef TZ_MAX_TIMES -#define TZ_MAX_TIMES 2000 -#endif /* !defined TZ_MAX_TIMES */ - -#ifndef TZ_MAX_TYPES -/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */ -#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */ -#endif /* !defined TZ_MAX_TYPES */ - -#ifndef TZ_MAX_CHARS -#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */ - /* (limited by what unsigned chars can hold) */ -#endif /* !defined TZ_MAX_CHARS */ - -#ifndef TZ_MAX_LEAPS -#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */ -#endif /* !defined TZ_MAX_LEAPS */ - -#define SECSPERMIN 60 -#define MINSPERHOUR 60 -#define HOURSPERDAY 24 -#define DAYSPERWEEK 7 -#define DAYSPERNYEAR 365 -#define DAYSPERLYEAR 366 -#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) -#define SECSPERDAY ((int_fast32_t)SECSPERHOUR * HOURSPERDAY) -#define MONSPERYEAR 12 - -#define TM_YEAR_BASE 1900 - -#define EPOCH_YEAR 1970 -#define EPOCH_WDAY 4 /* TM_THURSDAY */ - -#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) - -struct ttinfo { /* time type information */ - int_fast32_t tt_gmtoff; /* UT offset in seconds */ - int tt_isdst; /* used to set tm_isdst */ - int tt_abbrind; /* abbreviation list index */ - int tt_ttisstd; /* true if transition is std time */ - int tt_ttisgmt; /* true if transition is UT */ -}; - -struct lsinfo { /* leap second information */ - int64_t ls_trans; /* transition time */ - int_fast64_t ls_corr; /* correction to apply */ -}; - -#define BIGGEST(a, b) (((a) > (b)) ? (a) : (b)) - -#ifdef TZNAME_MAX -#define MY_TZNAME_MAX TZNAME_MAX -#endif /* defined TZNAME_MAX */ -#ifndef TZNAME_MAX -#define MY_TZNAME_MAX 255 -#endif /* !defined TZNAME_MAX */ - -struct state { - int leapcnt; - int timecnt; - int typecnt; - int charcnt; - int goback; - int goahead; - int64_t ats[TZ_MAX_TIMES]; - unsigned char types[TZ_MAX_TIMES]; - struct ttinfo ttis[TZ_MAX_TYPES]; - char chars[BIGGEST(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))]; - struct lsinfo lsis[TZ_MAX_LEAPS]; - int defaulttype; /* for early times or if no transitions */ -}; - -struct rule { - int r_type; /* type of rule--see below */ - int r_day; /* day number of rule */ - int r_week; /* week number of rule */ - int r_mon; /* month number of rule */ - int_fast32_t r_time; /* transition time of rule */ -}; - -/* -** Prototypes for static functions. -*/ - -static void -gmtload(struct state *const sp); -static struct bson_tm * -gmtsub(const int64_t *const timep, const int_fast32_t offset, struct bson_tm *const tmp); -static int64_t -increment_overflow(int64_t *const ip, int64_t j); -static int64_t -leaps_thru_end_of(const int64_t y) ATTRIBUTE_PURE; -static int64_t -increment_overflow32(int_fast32_t *const lp, int64_t const m); -static int64_t -normalize_overflow32(int_fast32_t *const tensptr, int64_t *const unitsptr, const int64_t base); -static int64_t -normalize_overflow(int64_t *const tensptr, int64_t *const unitsptr, const int64_t base); -static int64_t -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 *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 *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 *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 *const atmp, const struct bson_tm *const btmp); - -static struct state gmtmem; -#define gmtptr (&gmtmem) - -static int gmt_is_set; - -static const int mon_lengths[2][MONSPERYEAR] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, - {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; - -static const int year_lengths[2] = {DAYSPERNYEAR, DAYSPERLYEAR}; - -static void -gmtload(struct state *const sp) -{ - memset(sp, 0, sizeof(struct state)); - sp->typecnt = 1; - sp->charcnt = 4; - sp->chars[0] = 'G'; - sp->chars[1] = 'M'; - sp->chars[2] = 'T'; -} - -/* -** gmtsub is to gmtime as localsub is to localtime. -*/ - -static struct bson_tm * -gmtsub(const int64_t *const timep, const int_fast32_t offset, struct bson_tm *const tmp) -{ - struct bson_tm *result; - - if (!gmt_is_set) { - gmt_is_set = true; - gmtload(gmtptr); - } - result = timesub(timep, offset, gmtptr, tmp); - return result; -} - -/* -** Return the number of leap years through the end of the given year -** where, to make the math easy, the answer for year zero is defined as zero. -*/ - -static int64_t -leaps_thru_end_of(const int64_t y) -{ - return (y >= 0) ? (y / 4 - y / 100 + y / 400) : -(leaps_thru_end_of(-(y + 1)) + 1); -} - -static struct bson_tm * -timesub(const int64_t *const timep, const int_fast32_t offset, const struct state *const sp, struct bson_tm *const tmp) -{ - const struct lsinfo *lp; - int64_t tdays; - int64_t idays; /* unsigned would be so 2003 */ - int_fast64_t rem; - int64_t y; - const int (*ip)[MONSPERYEAR]; - int_fast64_t corr; - int64_t hit; - int64_t i; - - corr = 0; - hit = 0; - i = (sp == NULL) ? 0 : sp->leapcnt; - while (--i >= 0) { - lp = &sp->lsis[i]; - if (*timep >= lp->ls_trans) { - if (*timep == lp->ls_trans) { - hit = ((i == 0 && lp->ls_corr > 0) || lp->ls_corr > sp->lsis[i - 1].ls_corr); - if (hit) - while (i > 0 && sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 && - sp->lsis[i].ls_corr == sp->lsis[i - 1].ls_corr + 1) { - ++hit; - --i; - } - } - corr = lp->ls_corr; - break; - } - } - y = EPOCH_YEAR; - tdays = *timep / SECSPERDAY; - rem = *timep - tdays * SECSPERDAY; - while (tdays < 0 || tdays >= year_lengths[isleap(y)]) { - int64_t newy; - int64_t tdelta; - int64_t idelta; - int64_t leapdays; - - tdelta = tdays / DAYSPERLYEAR; - idelta = tdelta; - if (idelta == 0) - idelta = (tdays < 0) ? -1 : 1; - newy = y; - if (increment_overflow(&newy, idelta)) - return NULL; - leapdays = leaps_thru_end_of(newy - 1) - leaps_thru_end_of(y - 1); - tdays -= ((int64_t)newy - y) * DAYSPERNYEAR; - tdays -= leapdays; - y = newy; - } - { - int_fast32_t seconds; - - seconds = (int_fast32_t)(tdays * SECSPERDAY); - tdays = seconds / SECSPERDAY; - rem += seconds - tdays * SECSPERDAY; - } - /* - ** Given the range, we can now fearlessly cast... - */ - idays = (int64_t)tdays; - rem += offset - corr; - while (rem < 0) { - rem += SECSPERDAY; - --idays; - } - while (rem >= SECSPERDAY) { - rem -= SECSPERDAY; - ++idays; - } - while (idays < 0) { - if (increment_overflow(&y, -1)) - return NULL; - idays += year_lengths[isleap(y)]; - } - while (idays >= year_lengths[isleap(y)]) { - idays -= year_lengths[isleap(y)]; - if (increment_overflow(&y, 1)) - return NULL; - } - tmp->tm_year = y; - if (increment_overflow(&tmp->tm_year, -TM_YEAR_BASE)) - return NULL; - tmp->tm_yday = idays; - /* - ** The "extra" mods below avoid overflow problems. - */ - tmp->tm_wday = EPOCH_WDAY + ((y - EPOCH_YEAR) % DAYSPERWEEK) * (DAYSPERNYEAR % DAYSPERWEEK) + - leaps_thru_end_of(y - 1) - leaps_thru_end_of(EPOCH_YEAR - 1) + idays; - tmp->tm_wday %= DAYSPERWEEK; - if (tmp->tm_wday < 0) - tmp->tm_wday += DAYSPERWEEK; - tmp->tm_hour = (int64_t)(rem / SECSPERHOUR); - rem %= SECSPERHOUR; - tmp->tm_min = (int64_t)(rem / SECSPERMIN); - /* - ** A positive leap second requires a special - ** representation. This uses "... ??:59:60" et seq. - */ - tmp->tm_sec = (int64_t)(rem % SECSPERMIN) + hit; - 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 - tmp->TM_GMTOFF = offset; -#endif /* defined TM_GMTOFF */ - return tmp; -} - -/* -** Adapted from code provided by Robert Elz, who writes: -** The "best" way to do mktime I think is based on an idea of Bob -** Kridle's (so its said...) from a long time ago. -** It does a binary search of the int64_t space. Since int64_t's are -** just 32 bits, its a max of 32 iterations (even at 64 bits it -** would still be very reasonable). -*/ - -#ifndef WRONG -#define WRONG (-1) -#endif /* !defined WRONG */ - -/* -** Normalize logic courtesy Paul Eggert. -*/ - -static int64_t -increment_overflow(int64_t *const ip, int64_t j) -{ - int64_t const i = *ip; - - /* - ** If i >= 0 there can only be overflow if i + j > INT_MAX - ** or if j > INT_MAX - i; given i >= 0, INT_MAX - i cannot overflow. - ** If i < 0 there can only be overflow if i + j < INT_MIN - ** or if j < INT_MIN - i; given i < 0, INT_MIN - i cannot overflow. - */ - if ((i >= 0) ? (j > INT_MAX - i) : (j < INT_MIN - i)) - return true; - *ip += j; - return false; -} - -static int64_t -increment_overflow32(int_fast32_t *const lp, int64_t const m) -{ - int_fast32_t const l = *lp; - - if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l)) - return true; - *lp += (int_fast32_t)m; - return false; -} - -static int64_t -normalize_overflow(int64_t *const tensptr, int64_t *const unitsptr, const int64_t base) -{ - int64_t tensdelta; - - tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base); - *unitsptr -= tensdelta * base; - return increment_overflow(tensptr, tensdelta); -} - -static int64_t -normalize_overflow32(int_fast32_t *const tensptr, int64_t *const unitsptr, const int64_t base) -{ - int64_t tensdelta; - - tensdelta = (*unitsptr >= 0) ? (*unitsptr / base) : (-1 - (-1 - *unitsptr) / base); - *unitsptr -= tensdelta * base; - return increment_overflow32(tensptr, tensdelta); -} - -static int64_t -tmcomp(const struct bson_tm *const atmp, const struct bson_tm *const btmp) -{ - int64_t result; - - if (atmp->tm_year != btmp->tm_year) - return atmp->tm_year < btmp->tm_year ? -1 : 1; - if ((result = (atmp->tm_mon - btmp->tm_mon)) == 0 && (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && - (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && (result = (atmp->tm_min - btmp->tm_min)) == 0) - result = atmp->tm_sec - btmp->tm_sec; - return result; -} - -static int64_t -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) -{ - 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; - struct bson_tm yourtm, mytm; - - *okayp = false; - yourtm = *tmp; - if (do_norm_secs) { - if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec, SECSPERMIN)) - return WRONG; - } - if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR)) - return WRONG; - if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY)) - return WRONG; - y = (int_fast32_t)yourtm.tm_year; - if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR)) - return WRONG; - /* - ** Turn y into an actual year number for now. - ** It is converted back to an offset from TM_YEAR_BASE later. - */ - if (increment_overflow32(&y, TM_YEAR_BASE)) - return WRONG; - while (yourtm.tm_mday <= 0) { - if (increment_overflow32(&y, -1)) - return WRONG; - li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday += year_lengths[isleap(li)]; - } - while (yourtm.tm_mday > DAYSPERLYEAR) { - li = y + (1 < yourtm.tm_mon); - yourtm.tm_mday -= year_lengths[isleap(li)]; - if (increment_overflow32(&y, 1)) - return WRONG; - } - for (;;) { - i = mon_lengths[isleap(y)][yourtm.tm_mon]; - if (yourtm.tm_mday <= i) - break; - yourtm.tm_mday -= i; - if (++yourtm.tm_mon >= MONSPERYEAR) { - yourtm.tm_mon = 0; - if (increment_overflow32(&y, 1)) - return WRONG; - } - } - if (increment_overflow32(&y, -TM_YEAR_BASE)) - return WRONG; - yourtm.tm_year = y; - if (yourtm.tm_year != y) - return WRONG; - if (yourtm.tm_sec >= 0 && yourtm.tm_sec < SECSPERMIN) - saved_seconds = 0; - else if (y + TM_YEAR_BASE < EPOCH_YEAR) { - /* - ** We can't set tm_sec to 0, because that might push the - ** time below the minimum representable time. - ** Set tm_sec to 59 instead. - ** This assumes that the minimum representable time is - ** not in the same minute that a leap second was deleted from, - ** which is a safer assumption than using 58 would be. - */ - if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN)) - return WRONG; - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = SECSPERMIN - 1; - } else { - saved_seconds = yourtm.tm_sec; - yourtm.tm_sec = 0; - } - /* - ** Do a binary search. - */ - lo = INT64_MIN; - hi = INT64_MAX; - - for (;;) { - t = lo / 2 + hi / 2; - if (t < lo) - t = lo; - else if (t > hi) - t = hi; - if ((*funcp)(&t, offset, &mytm) == NULL) { - /* - ** Assume that t is too extreme to be represented in - ** a struct bson_tm; arrange things so that it is less - ** extreme on the next pass. - */ - dir = (t > 0) ? 1 : -1; - } else - dir = tmcomp(&mytm, &yourtm); - if (dir != 0) { - if (t == lo) { - if (t == time_t_max) - return WRONG; - ++t; - ++lo; - } else if (t == hi) { - if (t == time_t_min) - return WRONG; - --t; - --hi; - } - if (lo > hi) - return WRONG; - if (dir > 0) - hi = t; - else - lo = t; - continue; - } - if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst) - break; - /* - ** Right time, wrong type. - ** Hunt for right time, right type. - ** It's okay to guess wrong since the guess - ** gets checked. - */ - sp = (const struct state *)gmtptr; - if (sp == NULL) - return WRONG; - for (i = sp->typecnt - 1; i >= 0; --i) { - if (sp->ttis[i].tt_isdst != yourtm.tm_isdst) - continue; - for (j = sp->typecnt - 1; j >= 0; --j) { - if (sp->ttis[j].tt_isdst == yourtm.tm_isdst) - continue; - newt = t + sp->ttis[j].tt_gmtoff - sp->ttis[i].tt_gmtoff; - if ((*funcp)(&newt, offset, &mytm) == NULL) - continue; - if (tmcomp(&mytm, &yourtm) != 0) - continue; - if (mytm.tm_isdst != yourtm.tm_isdst) - continue; - /* - ** We have a match. - */ - t = newt; - goto label; - } - } - return WRONG; - } -label: - newt = t + saved_seconds; - if ((newt < t) != (saved_seconds < 0)) - return WRONG; - t = newt; - if ((*funcp)(&t, offset, tmp)) - *okayp = true; - return t; -} - -static int64_t -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) -{ - int64_t t; - - /* - ** First try without normalization of seconds - ** (in case tm_sec contains a value associated with a leap second). - ** If that fails, try with normalization of seconds. - */ - t = time2sub(tmp, funcp, offset, okayp, false); - return *okayp ? t : time2sub(tmp, funcp, offset, okayp, true); -} - -static int64_t -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) -{ - 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; - - if (tmp == NULL) { - errno = EINVAL; - return WRONG; - } - if (tmp->tm_isdst > 1) - tmp->tm_isdst = 1; - t = time2(tmp, funcp, offset, &okay); - if (okay) - return t; - if (tmp->tm_isdst < 0) -#ifdef PCTS - /* - ** POSIX Conformance Test Suite code courtesy Grant Sullivan. - */ - tmp->tm_isdst = 0; /* reset to std and try again */ -#else - return t; -#endif /* !defined PCTS */ - /* - ** We're supposed to assume that somebody took a time of one type - ** and did some math on it that yielded a "struct tm" that's bad. - ** We try to divine the type they started from and adjust to the - ** type they need. - */ - sp = (const struct state *)gmtptr; - if (sp == NULL) - return WRONG; - for (i = 0; i < sp->typecnt; ++i) - seen[i] = false; - nseen = 0; - for (i = sp->timecnt - 1; i >= 0; --i) - if (!seen[sp->types[i]]) { - seen[sp->types[i]] = true; - types[nseen++] = sp->types[i]; - } - for (sameind = 0; sameind < nseen; ++sameind) { - samei = types[sameind]; - if (sp->ttis[samei].tt_isdst != tmp->tm_isdst) - continue; - for (otherind = 0; otherind < nseen; ++otherind) { - otheri = types[otherind]; - if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst) - continue; - tmp->tm_sec += sp->ttis[otheri].tt_gmtoff - sp->ttis[samei].tt_gmtoff; - tmp->tm_isdst = !tmp->tm_isdst; - t = time2(tmp, funcp, offset, &okay); - if (okay) - return t; - tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff - sp->ttis[samei].tt_gmtoff; - tmp->tm_isdst = !tmp->tm_isdst; - } - } - return WRONG; -} - -int64_t -_bson_timegm(struct bson_tm *const tmp) -{ - if (tmp != NULL) - tmp->tm_isdst = 0; - return time1(tmp, gmtsub, 0L); -} diff --git a/bsonjs/bson/bson-types.h b/bsonjs/bson/bson-types.h deleted file mode 100644 index 9378a2b..0000000 --- a/bsonjs/bson/bson-types.h +++ /dev/null @@ -1,475 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_TYPES_H -#define BSON_TYPES_H - -#include -#include -#include -#include -#include -#include - -#include - -#include - -BSON_BEGIN_DECLS - - -/* - *-------------------------------------------------------------------------- - * - * bson_unichar_t -- - * - * bson_unichar_t provides an unsigned 32-bit type for containing - * unicode characters. When iterating UTF-8 sequences, this should - * be used to avoid losing the high-bits of non-ascii characters. - * - *-------------------------------------------------------------------------- - */ - -typedef uint32_t bson_unichar_t; - - -/** - * @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), - /* Deprecated: Does nothing */ - BSON_CONTEXT_USE_TASK_ID = (1 << 3), -} bson_context_flags_t; - - -/** - * bson_context_t: - * - * This structure manages context for the bson library. It handles - * configuration for thread-safety and other performance related requirements. - * Consumers will create a context and may use multiple under a variety of - * situations. - * - * If your program calls fork(), you should initialize a new bson_context_t - * using bson_context_init(). - * - * If you are using threading, it is suggested that you use a bson_context_t - * per thread for best performance. Alternatively, you can initialize the - * bson_context_t with BSON_CONTEXT_THREAD_SAFE, although a performance penalty - * will be incurred. - * - * Many functions will require that you provide a bson_context_t such as OID - * generation. - * - * This structure is opaque in that you cannot see the contents of the - * structure. However, it is stack allocatable in that enough padding is - * provided in _bson_context_t to hold the structure. - */ -typedef struct _bson_context_t bson_context_t; - -/** - * bson_json_opts_t: - * - * This structure is used to pass options for serializing BSON into extended - * JSON to the respective serialization methods. - * - * max_len can be either a non-negative integer, or BSON_MAX_LEN_UNLIMITED to - * set no limit for serialization length. - */ -typedef struct _bson_json_opts_t bson_json_opts_t; - - -/** - * bson_oid_t: - * - * This structure contains the binary form of a BSON Object Id as specified - * on http://bsonspec.org. If you would like the bson_oid_t in string form - * see bson_oid_to_string() or bson_oid_to_string_r(). - */ -typedef struct bson_oid_t { - uint8_t bytes[12]; -} bson_oid_t; - -BSON_STATIC_ASSERT2(oid_t, sizeof(bson_oid_t) == 12); - -/** - * bson_decimal128_t: - * - * @high The high-order bytes of the decimal128. This field contains sign, - * combination bits, exponent, and part of the coefficient continuation. - * @low The low-order bytes of the decimal128. This field contains the second - * part of the coefficient continuation. - * - * This structure is a boxed type containing the value for the BSON decimal128 - * type. The structure stores the 128 bits such that they correspond to the - * native format for the IEEE decimal128 type, if it is implemented. - **/ -typedef struct { -#if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN - uint64_t low; - uint64_t high; -#elif BSON_BYTE_ORDER == BSON_BIG_ENDIAN - uint64_t high; - uint64_t low; -#endif -} bson_decimal128_t; - - -/** - * @brief Flags and error codes for BSON validation functions. - * - * Pass these flags bits to control the behavior of the `bson_validate` family - * of functions. - * - * Additionally, if validation fails, then the error code set on a `bson_error_t` - * will have the value corresponding to the reason that validation failed. - */ -typedef enum { - /** - * @brief No special validation behavior specified. - */ - BSON_VALIDATE_NONE = 0, - /** - * @brief Check that all text components of the BSON data are valid UTF-8. - * - * Note that this will also cause validation to reject valid text that contains - * a null character. This can be changed by also passing - * `BSON_VALIDATE_UTF8_ALLOW_NULL` - */ - BSON_VALIDATE_UTF8 = (1 << 0), - /** - * @brief Check that element keys do not begin with an ASCII dollar `$` - */ - BSON_VALIDATE_DOLLAR_KEYS = (1 << 1), - /** - * @brief Check that element keys do not contain an ASCII period `.` - */ - BSON_VALIDATE_DOT_KEYS = (1 << 2), - /** - * @brief If set then it is *not* an error for a UTF-8 string to contain - * embedded null characters. - * - * This has no effect unless `BSON_VALIDATE_UTF8` is also passed. - */ - BSON_VALIDATE_UTF8_ALLOW_NULL = (1 << 3), - /** - * @brief Check that no element key is a zero-length empty string. - */ - BSON_VALIDATE_EMPTY_KEYS = (1 << 4), - /** - * @brief This is not a flag that controls behavior, but is instead used to indicate - * that a BSON document is corrupted in some way. This is the value that will - * appear as an error code. - * - * Passing this as a flag has no effect. - */ - BSON_VALIDATE_CORRUPT = (1 << 5), -} bson_validate_flags_t; - - -/** - * bson_type_t: - * - * This enumeration contains all of the possible types within a BSON document. - * Use bson_iter_type() to fetch the type of a field while iterating over it. - */ -typedef enum { - BSON_TYPE_EOD = 0x00, - BSON_TYPE_DOUBLE = 0x01, - BSON_TYPE_UTF8 = 0x02, - BSON_TYPE_DOCUMENT = 0x03, - BSON_TYPE_ARRAY = 0x04, - BSON_TYPE_BINARY = 0x05, - BSON_TYPE_UNDEFINED = 0x06, - BSON_TYPE_OID = 0x07, - BSON_TYPE_BOOL = 0x08, - BSON_TYPE_DATE_TIME = 0x09, - BSON_TYPE_NULL = 0x0A, - BSON_TYPE_REGEX = 0x0B, - BSON_TYPE_DBPOINTER = 0x0C, - BSON_TYPE_CODE = 0x0D, - BSON_TYPE_SYMBOL = 0x0E, - BSON_TYPE_CODEWSCOPE = 0x0F, - BSON_TYPE_INT32 = 0x10, - BSON_TYPE_TIMESTAMP = 0x11, - BSON_TYPE_INT64 = 0x12, - BSON_TYPE_DECIMAL128 = 0x13, - BSON_TYPE_MAXKEY = 0x7F, - BSON_TYPE_MINKEY = 0xFF, -} bson_type_t; - - -/** - * bson_subtype_t: - * - * This enumeration contains the various subtypes that may be used in a binary - * field. See http://bsonspec.org for more information. - */ -typedef enum { - BSON_SUBTYPE_BINARY = 0x00, - BSON_SUBTYPE_FUNCTION = 0x01, - BSON_SUBTYPE_BINARY_DEPRECATED = 0x02, - BSON_SUBTYPE_UUID_DEPRECATED = 0x03, - BSON_SUBTYPE_UUID = 0x04, - BSON_SUBTYPE_MD5 = 0x05, - BSON_SUBTYPE_ENCRYPTED = 0x06, - BSON_SUBTYPE_COLUMN = 0x07, - BSON_SUBTYPE_SENSITIVE = 0x08, - BSON_SUBTYPE_VECTOR = 0x09, - BSON_SUBTYPE_USER = 0x80, -} bson_subtype_t; - - -/* - *-------------------------------------------------------------------------- - * - * bson_value_t -- - * - * A boxed type to contain various bson_type_t types. - * - * See also: - * bson_value_copy() - * bson_value_destroy() - * - *-------------------------------------------------------------------------- - */ - -typedef struct _bson_value_t { - bson_type_t value_type; - int32_t padding; - union { - bson_oid_t v_oid; - int64_t v_int64; - int32_t v_int32; - int8_t v_int8; - double v_double; - bool v_bool; - int64_t v_datetime; - struct { - uint32_t timestamp; - uint32_t increment; - } v_timestamp; - struct { - char *str; - uint32_t len; - } v_utf8; - struct { - uint8_t *data; - uint32_t data_len; - } v_doc; - struct { - uint8_t *data; - uint32_t data_len; - bson_subtype_t subtype; - } v_binary; - struct { - char *regex; - char *options; - } v_regex; - struct { - char *collection; - uint32_t collection_len; - bson_oid_t oid; - } v_dbpointer; - struct { - char *code; - uint32_t code_len; - } v_code; - struct { - char *code; - uint8_t *scope_data; - uint32_t code_len; - uint32_t scope_len; - } v_codewscope; - struct { - char *symbol; - uint32_t len; - } v_symbol; - bson_decimal128_t v_decimal128; - } value; -} bson_value_t; - - -/** - * bson_iter_t: - * - * This structure manages iteration over a bson_t structure. It keeps track - * of the location of the current key and value within the buffer. Using the - * various functions to get the value of the iter will read from these - * locations. - * - * This structure is safe to discard on the stack. No cleanup is necessary - * after using it. - */ -typedef struct bson_iter_t { - const uint8_t *raw; /* The raw buffer being iterated. */ - uint32_t len; /* The length of raw. */ - uint32_t off; /* The offset within the buffer. */ - uint32_t type; /* The offset of the type byte. */ - uint32_t key; /* The offset of the key byte. */ - uint32_t d1; /* The offset of the first data byte. */ - uint32_t d2; /* The offset of the second data byte. */ - uint32_t d3; /* The offset of the third data byte. */ - uint32_t d4; /* The offset of the fourth data byte. */ - uint32_t next_off; /* The offset of the next field. */ - uint32_t err_off; /* The offset of the error. */ - bson_value_t value; /* Internal value for various state. */ -} bson_iter_t; - - -/** - * bson_reader_t: - * - * This structure is used to iterate over a sequence of BSON documents. It - * allows for them to be iterated with the possibility of no additional - * memory allocations under certain circumstances such as reading from an - * incoming mongo packet. - */ -BSON_ALIGNED_BEGIN(BSON_ALIGN_OF_PTR) -typedef struct { - uint32_t type; - /**< private >**/ -} bson_reader_t BSON_ALIGNED_END(BSON_ALIGN_OF_PTR); - - -/** - * bson_visitor_t: - * - * This structure contains a series of pointers that can be executed for - * each field of a BSON document based on the field type. - * - * For example, if an int32 field is found, visit_int32 will be called. - * - * When visiting each field using bson_iter_visit_all(), you may provide a - * data pointer that will be provided with each callback. This might be useful - * if you are marshaling to another language. - * - * You may pre-maturely stop the visitation of fields by returning true in your - * visitor. Returning false will continue visitation to further fields. - */ -typedef struct { - /* run before / after descending into a document */ - bool(BSON_CALL *visit_before)(const bson_iter_t *iter, const char *key, void *data); - bool(BSON_CALL *visit_after)(const bson_iter_t *iter, const char *key, void *data); - /* corrupt BSON, or unsupported type and visit_unsupported_type not set */ - void(BSON_CALL *visit_corrupt)(const bson_iter_t *iter, void *data); - /* normal bson field callbacks */ - bool(BSON_CALL *visit_double)(const bson_iter_t *iter, const char *key, double v_double, void *data); - bool(BSON_CALL *visit_utf8)( - const bson_iter_t *iter, const char *key, size_t v_utf8_len, const char *v_utf8, void *data); - bool(BSON_CALL *visit_document)(const bson_iter_t *iter, const char *key, const bson_t *v_document, void *data); - bool(BSON_CALL *visit_array)(const bson_iter_t *iter, const char *key, const bson_t *v_array, void *data); - bool(BSON_CALL *visit_binary)(const bson_iter_t *iter, - const char *key, - bson_subtype_t v_subtype, - size_t v_binary_len, - const uint8_t *v_binary, - void *data); - /* normal field with deprecated "Undefined" BSON type */ - bool(BSON_CALL *visit_undefined)(const bson_iter_t *iter, const char *key, void *data); - bool(BSON_CALL *visit_oid)(const bson_iter_t *iter, const char *key, const bson_oid_t *v_oid, void *data); - bool(BSON_CALL *visit_bool)(const bson_iter_t *iter, const char *key, bool v_bool, void *data); - bool(BSON_CALL *visit_date_time)(const bson_iter_t *iter, const char *key, int64_t msec_since_epoch, void *data); - bool(BSON_CALL *visit_null)(const bson_iter_t *iter, const char *key, void *data); - bool(BSON_CALL *visit_regex)( - const bson_iter_t *iter, const char *key, const char *v_regex, const char *v_options, void *data); - bool(BSON_CALL *visit_dbpointer)(const bson_iter_t *iter, - const char *key, - size_t v_collection_len, - const char *v_collection, - const bson_oid_t *v_oid, - void *data); - bool(BSON_CALL *visit_code)( - const bson_iter_t *iter, const char *key, size_t v_code_len, const char *v_code, void *data); - bool(BSON_CALL *visit_symbol)( - const bson_iter_t *iter, const char *key, size_t v_symbol_len, const char *v_symbol, void *data); - bool(BSON_CALL *visit_codewscope)(const bson_iter_t *iter, - const char *key, - size_t v_code_len, - const char *v_code, - const bson_t *v_scope, - void *data); - bool(BSON_CALL *visit_int32)(const bson_iter_t *iter, const char *key, int32_t v_int32, void *data); - bool(BSON_CALL *visit_timestamp)( - const bson_iter_t *iter, const char *key, uint32_t v_timestamp, uint32_t v_increment, void *data); - bool(BSON_CALL *visit_int64)(const bson_iter_t *iter, const char *key, int64_t v_int64, void *data); - bool(BSON_CALL *visit_maxkey)(const bson_iter_t *iter, const char *key, void *data); - bool(BSON_CALL *visit_minkey)(const bson_iter_t *iter, const char *key, void *data); - /* if set, called instead of visit_corrupt when an apparently valid BSON - * includes an unrecognized field type (reading future version of BSON) */ - void(BSON_CALL *visit_unsupported_type)(const bson_iter_t *iter, const char *key, uint32_t type_code, void *data); - bool(BSON_CALL *visit_decimal128)(const bson_iter_t *iter, - const char *key, - const bson_decimal128_t *v_decimal128, - void *data); - - void *padding[7]; -} bson_visitor_t; - - -/** - * bson_next_power_of_two: - * @v: A 32-bit unsigned integer of required bytes. - * - * Determines the next larger power of two for the value of @v - * in a constant number of operations. - * - * It is up to the caller to guarantee this will not overflow. - * - * Returns: The next power of 2 from @v. - */ -static BSON_INLINE size_t -bson_next_power_of_two(size_t v) -{ - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; -#if BSON_WORD_SIZE == 64 - v |= v >> 32; -#endif - v++; - - return v; -} - - -static BSON_INLINE bool -bson_is_power_of_two(uint32_t v) -{ - return ((v != 0) && ((v & (v - 1)) == 0)); -} - - -BSON_END_DECLS - - -#endif /* BSON_TYPES_H */ diff --git a/bsonjs/bson/bson-utf8.c b/bsonjs/bson/bson-utf8.c deleted file mode 100644 index 6ad08f4..0000000 --- a/bsonjs/bson/bson-utf8.c +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include - -#include -#include -#include -#include - -#include - -#include - -#include - - -/* - *-------------------------------------------------------------------------- - * - * bson_utf8_validate -- - * - * Validates that @utf8 is a valid UTF-8 string. Note that we only - * support UTF-8 characters which have sequence length less than or equal - * to 4 bytes (RFC 3629). - * - * If @allow_null is true, then \0 is allowed within @utf8_len bytes - * of @utf8. Generally, this is bad practice since the main point of - * UTF-8 strings is that they can be used with strlen() and friends. - * However, some languages such as Python can send UTF-8 encoded - * strings with NUL's in them. - * - * Note that the two-byte sequence "C0 80" is also interpreted as an - * internal NUL, for historical reasons. This sequence is considered - * invalid according to RFC3629. - * - * Parameters: - * @utf8: A UTF-8 encoded string. - * @utf8_len: The length of @utf8 in bytes. - * @allow_null: If the single "00" byte or two-byte sequence "C0 80" are allowed internally within @utf8. - * - * Returns: - * true if @utf8 is valid UTF-8. otherwise false. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_utf8_validate(const char *utf8, /* IN */ - size_t utf8_len, /* IN */ - bool allow_null) /* IN */ -{ - bson_unichar_t c; - uint8_t first_mask; - uint8_t seq_length; - size_t i; - size_t j; - - BSON_ASSERT(utf8); - - for (i = 0; i < utf8_len; i += seq_length) { - mcommon_utf8_get_sequence(&utf8[i], &seq_length, &first_mask); - - /* - * Ensure we have a valid multi-byte sequence length. - */ - if (!seq_length) { - return false; - } - - /* - * Ensure we have enough bytes left. - */ - if ((utf8_len - i) < seq_length) { - return false; - } - - /* - * Also calculate the next char as a unichar so we can - * check code ranges for non-shortest form. - */ - c = utf8[i] & first_mask; - - /* - * Check the high-bits for each additional sequence byte. - */ - for (j = i + 1; j < (i + seq_length); j++) { - c = (c << 6) | (utf8[j] & 0x3F); - if ((utf8[j] & 0xC0) != 0x80) { - return false; - } - } - - /* - * Check for NULL bytes afterwards. - * - * Hint: if you want to optimize this function, starting here to do - * this in the same pass as the data above would probably be a good - * idea. You would add a branch into the inner loop, but save possibly - * on cache-line bouncing on larger strings. Just a thought. - */ - if (!allow_null) { - for (j = 0; j < seq_length; j++) { - if (((i + j) > utf8_len) || !utf8[i + j]) { - return false; - } - } - } - - /* - * Code point won't fit in utf-16, not allowed. - */ - if (c > 0x0010FFFF) { - return false; - } - - /* - * Byte is in reserved range for UTF-16 high-marks - * for surrogate pairs. - */ - if ((c & 0xFFFFF800) == 0xD800) { - return false; - } - - /* - * Check non-shortest form unicode. - */ - switch (seq_length) { - case 1: - if (c <= 0x007F) { - continue; - } - return false; - - case 2: - if ((c >= 0x0080) && (c <= 0x07FF)) { - continue; - } else if (c == 0) { - /* Two-byte representation for NULL. */ - if (!allow_null) { - return false; - } - continue; - } - return false; - - case 3: - if (((c >= 0x0800) && (c <= 0x0FFF)) || ((c >= 0x1000) && (c <= 0xFFFF))) { - continue; - } - return false; - - case 4: - if (((c >= 0x10000) && (c <= 0x3FFFF)) || ((c >= 0x40000) && (c <= 0xFFFFF)) || - ((c >= 0x100000) && (c <= 0x10FFFF))) { - continue; - } - return false; - - default: - return false; - } - } - - return true; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_utf8_escape_for_json -- - * - * Allocates a new string matching @utf8 except that special - * characters in JSON will be escaped. The resulting string is also - * UTF-8 encoded. - * - * Both " and \ characters will be escaped. Additionally, if a NUL - * byte is found before @utf8_len bytes, it will be converted to the - * two byte UTF-8 sequence. - * - * The two-byte sequence "C0 80" is also interpreted as an internal NUL, - * for historical reasons. This sequence is considered invalid according - * to RFC3629. - * - * Parameters: - * @utf8: A UTF-8 encoded string. - * @utf8_len: The length of @utf8 in bytes or -1 if NUL terminated. - * - * Returns: - * A newly allocated string that should be freed with bson_free(). - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -char * -bson_utf8_escape_for_json(const char *utf8, /* IN */ - ssize_t utf8_len) /* IN */ -{ - uint32_t len32; - bool allow_nul; - - if (utf8_len < 0) { - size_t sizet_len = strlen(utf8); - if (sizet_len < UINT32_MAX) { - len32 = (uint32_t)sizet_len; - allow_nul = false; - } else { - return NULL; - } - } else { - if (mlib_in_range(uint32_t, utf8_len) && (uint32_t)utf8_len < UINT32_MAX) { - len32 = utf8_len; - allow_nul = true; - } else { - return NULL; - } - } - - /* The new private implementation of mcommon_json_append_escaped() avoids - * parsing UTF-8 sequences at all in most cases. It preserves the validity - * of valid sequences, but it will not catch most UTF-8 errors. For compatibility - * at the expense of performance, we emulate the old behavior in this wrapper. - */ - if (!bson_utf8_validate(utf8, (size_t)len32, allow_nul)) { - return NULL; - } - - mcommon_string_append_t append; - mcommon_string_new_with_capacity_as_append(&append, len32); - if (mcommon_json_append_escaped(&append, utf8, len32, allow_nul)) { - return mcommon_string_from_append_destroy_with_steal(&append); - } else { - mcommon_string_from_append_destroy(&append); - return NULL; - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_utf8_get_char -- - * - * Fetches the next UTF-8 character from the UTF-8 sequence. - * - * Parameters: - * @utf8: A string containing validated UTF-8. - * - * Returns: - * A 32-bit bson_unichar_t reprsenting the multi-byte sequence. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bson_unichar_t -bson_utf8_get_char(const char *utf8) /* IN */ -{ - bson_unichar_t c; - uint8_t mask; - uint8_t num; - int i; - - BSON_ASSERT(utf8); - - mcommon_utf8_get_sequence(utf8, &num, &mask); - c = (*utf8) & mask; - - for (i = 1; i < num; i++) { - c = (c << 6) | (utf8[i] & 0x3F); - } - - return c; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_utf8_next_char -- - * - * Returns an incremented pointer to the beginning of the next - * multi-byte sequence in @utf8. - * - * Parameters: - * @utf8: A string containing validated UTF-8. - * - * Returns: - * An incremented pointer in @utf8. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -const char * -bson_utf8_next_char(const char *utf8) /* IN */ -{ - uint8_t mask; - uint8_t num; - - BSON_ASSERT(utf8); - - mcommon_utf8_get_sequence(utf8, &num, &mask); - - return utf8 + num; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_utf8_from_unichar -- - * - * Converts the unichar to a sequence of utf8 bytes and stores those - * in @utf8. The number of bytes in the sequence are stored in @len. - * - * Parameters: - * @unichar: A bson_unichar_t. - * @utf8: A location for the multi-byte sequence. - * @len: A location for number of bytes stored in @utf8. - * - * Returns: - * None. - * - * Side effects: - * @utf8 is set. - * @len is set. - * - *-------------------------------------------------------------------------- - */ - -void -bson_utf8_from_unichar(bson_unichar_t unichar, /* IN */ - char utf8[BSON_ENSURE_ARRAY_PARAM_SIZE(6)], /* OUT */ - uint32_t *len) /* OUT */ -{ - // Inlined implementation from common-utf8-private - mcommon_utf8_from_unichar(unichar, utf8, len); -} diff --git a/bsonjs/bson/bson-utf8.h b/bsonjs/bson/bson-utf8.h deleted file mode 100644 index 7720879..0000000 --- a/bsonjs/bson/bson-utf8.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_UTF8_H -#define BSON_UTF8_H - - -#include -#include - - -BSON_BEGIN_DECLS - - -BSON_EXPORT(bool) -bson_utf8_validate(const char *utf8, size_t utf8_len, bool allow_null); -BSON_EXPORT(char *) -bson_utf8_escape_for_json(const char *utf8, ssize_t utf8_len); -BSON_EXPORT(bson_unichar_t) -bson_utf8_get_char(const char *utf8); -BSON_EXPORT(const char *) -bson_utf8_next_char(const char *utf8); -BSON_EXPORT(void) -bson_utf8_from_unichar(bson_unichar_t unichar, char utf8[6], uint32_t *len); - - -BSON_END_DECLS - - -#endif /* BSON_UTF8_H */ diff --git a/bsonjs/bson/bson-value.c b/bsonjs/bson/bson-value.c deleted file mode 100644 index 3a38e70..0000000 --- a/bsonjs/bson/bson-value.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include - -#include - -#include -#include - -#include - - -void -bson_value_copy(const bson_value_t *src, /* IN */ - bson_value_t *dst) /* OUT */ -{ - BSON_ASSERT(src); - BSON_ASSERT(dst); - - dst->value_type = src->value_type; - - switch (src->value_type) { - case BSON_TYPE_DOUBLE: - dst->value.v_double = src->value.v_double; - break; - case BSON_TYPE_UTF8: - BSON_ASSERT(mlib_in_range(size_t, src->value.v_utf8.len)); - size_t utf8_len_sz = (size_t)src->value.v_utf8.len; - if (utf8_len_sz == SIZE_MAX) { - // If the string is at maximum length, do not NULL terminate. The source necessarily cannot fit it. - dst->value.v_utf8.len = src->value.v_utf8.len; - dst->value.v_utf8.str = bson_malloc(utf8_len_sz); - memcpy(dst->value.v_utf8.str, src->value.v_utf8.str, dst->value.v_utf8.len); - } else { - // There is room in destination to NULL terminate. - dst->value.v_utf8.len = src->value.v_utf8.len; - dst->value.v_utf8.str = bson_malloc(utf8_len_sz + 1); - memcpy(dst->value.v_utf8.str, src->value.v_utf8.str, dst->value.v_utf8.len); - dst->value.v_utf8.str[dst->value.v_utf8.len] = '\0'; - } - break; - case BSON_TYPE_DOCUMENT: - case BSON_TYPE_ARRAY: - dst->value.v_doc.data_len = src->value.v_doc.data_len; - dst->value.v_doc.data = bson_malloc(src->value.v_doc.data_len); - memcpy(dst->value.v_doc.data, src->value.v_doc.data, dst->value.v_doc.data_len); - break; - case BSON_TYPE_BINARY: - dst->value.v_binary.subtype = src->value.v_binary.subtype; - dst->value.v_binary.data_len = src->value.v_binary.data_len; - dst->value.v_binary.data = bson_malloc(src->value.v_binary.data_len); - if (dst->value.v_binary.data_len) { - memcpy(dst->value.v_binary.data, src->value.v_binary.data, dst->value.v_binary.data_len); - } - break; - case BSON_TYPE_OID: - bson_oid_copy(&src->value.v_oid, &dst->value.v_oid); - break; - case BSON_TYPE_BOOL: - dst->value.v_bool = src->value.v_bool; - break; - case BSON_TYPE_DATE_TIME: - dst->value.v_datetime = src->value.v_datetime; - break; - case BSON_TYPE_REGEX: - dst->value.v_regex.regex = bson_strdup(src->value.v_regex.regex); - dst->value.v_regex.options = bson_strdup(src->value.v_regex.options); - break; - case BSON_TYPE_DBPOINTER: - BSON_ASSERT(mlib_in_range(size_t, src->value.v_dbpointer.collection_len)); - size_t dbpointer_len_sz = (size_t)src->value.v_dbpointer.collection_len; - BSON_ASSERT(dbpointer_len_sz <= SIZE_MAX - 1); - dst->value.v_dbpointer.collection_len = src->value.v_dbpointer.collection_len; - dst->value.v_dbpointer.collection = bson_malloc(dbpointer_len_sz + 1); - memcpy( - dst->value.v_dbpointer.collection, src->value.v_dbpointer.collection, dst->value.v_dbpointer.collection_len); - dst->value.v_dbpointer.collection[dst->value.v_dbpointer.collection_len] = '\0'; - bson_oid_copy(&src->value.v_dbpointer.oid, &dst->value.v_dbpointer.oid); - break; - case BSON_TYPE_CODE: - BSON_ASSERT(mlib_in_range(size_t, src->value.v_code.code_len)); - size_t code_len_sz = (size_t)src->value.v_code.code_len; - BSON_ASSERT(code_len_sz <= SIZE_MAX - 1); - dst->value.v_code.code_len = src->value.v_code.code_len; - dst->value.v_code.code = bson_malloc(code_len_sz + 1); - memcpy(dst->value.v_code.code, src->value.v_code.code, dst->value.v_code.code_len); - dst->value.v_code.code[dst->value.v_code.code_len] = '\0'; - break; - case BSON_TYPE_SYMBOL: - BSON_ASSERT(mlib_in_range(size_t, src->value.v_symbol.len)); - size_t symbol_len_sz = (size_t)src->value.v_symbol.len; - BSON_ASSERT(symbol_len_sz <= SIZE_MAX - 1); - dst->value.v_symbol.len = src->value.v_symbol.len; - dst->value.v_symbol.symbol = bson_malloc(symbol_len_sz + 1); - memcpy(dst->value.v_symbol.symbol, src->value.v_symbol.symbol, dst->value.v_symbol.len); - dst->value.v_symbol.symbol[dst->value.v_symbol.len] = '\0'; - break; - case BSON_TYPE_CODEWSCOPE: - BSON_ASSERT(mlib_in_range(size_t, src->value.v_codewscope.code_len)); - size_t codewscope_len_sz = (size_t)src->value.v_codewscope.code_len; - BSON_ASSERT(codewscope_len_sz <= SIZE_MAX - 1); - dst->value.v_codewscope.code_len = src->value.v_codewscope.code_len; - dst->value.v_codewscope.code = bson_malloc(codewscope_len_sz + 1); - memcpy(dst->value.v_codewscope.code, src->value.v_codewscope.code, dst->value.v_codewscope.code_len); - dst->value.v_codewscope.code[dst->value.v_codewscope.code_len] = '\0'; - dst->value.v_codewscope.scope_len = src->value.v_codewscope.scope_len; - dst->value.v_codewscope.scope_data = bson_malloc(src->value.v_codewscope.scope_len); - memcpy(dst->value.v_codewscope.scope_data, src->value.v_codewscope.scope_data, dst->value.v_codewscope.scope_len); - break; - case BSON_TYPE_INT32: - dst->value.v_int32 = src->value.v_int32; - break; - case BSON_TYPE_TIMESTAMP: - dst->value.v_timestamp.timestamp = src->value.v_timestamp.timestamp; - dst->value.v_timestamp.increment = src->value.v_timestamp.increment; - break; - case BSON_TYPE_INT64: - dst->value.v_int64 = src->value.v_int64; - break; - case BSON_TYPE_DECIMAL128: - dst->value.v_decimal128 = src->value.v_decimal128; - break; - case BSON_TYPE_UNDEFINED: - case BSON_TYPE_NULL: - case BSON_TYPE_MAXKEY: - case BSON_TYPE_MINKEY: - break; - case BSON_TYPE_EOD: - default: - BSON_ASSERT(false); - return; - } -} - - -void -bson_value_destroy(bson_value_t *value) /* IN */ -{ - if (!value) { - return; - } - - switch (value->value_type) { - case BSON_TYPE_UTF8: - bson_free(value->value.v_utf8.str); - break; - case BSON_TYPE_DOCUMENT: - case BSON_TYPE_ARRAY: - bson_free(value->value.v_doc.data); - break; - case BSON_TYPE_BINARY: - bson_free(value->value.v_binary.data); - break; - case BSON_TYPE_REGEX: - bson_free(value->value.v_regex.regex); - bson_free(value->value.v_regex.options); - break; - case BSON_TYPE_DBPOINTER: - bson_free(value->value.v_dbpointer.collection); - break; - case BSON_TYPE_CODE: - bson_free(value->value.v_code.code); - break; - case BSON_TYPE_SYMBOL: - bson_free(value->value.v_symbol.symbol); - break; - case BSON_TYPE_CODEWSCOPE: - bson_free(value->value.v_codewscope.code); - bson_free(value->value.v_codewscope.scope_data); - break; - case BSON_TYPE_DOUBLE: - case BSON_TYPE_UNDEFINED: - case BSON_TYPE_OID: - case BSON_TYPE_BOOL: - case BSON_TYPE_DATE_TIME: - case BSON_TYPE_NULL: - case BSON_TYPE_INT32: - case BSON_TYPE_TIMESTAMP: - case BSON_TYPE_INT64: - case BSON_TYPE_DECIMAL128: - case BSON_TYPE_MAXKEY: - case BSON_TYPE_MINKEY: - case BSON_TYPE_EOD: - default: - break; - } -} diff --git a/bsonjs/bson/bson-value.h b/bsonjs/bson/bson-value.h deleted file mode 100644 index cbd66d7..0000000 --- a/bsonjs/bson/bson-value.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_VALUE_H -#define BSON_VALUE_H - - -#include -#include - - -BSON_BEGIN_DECLS - - -BSON_EXPORT(void) -bson_value_copy(const bson_value_t *src, bson_value_t *dst); -BSON_EXPORT(void) -bson_value_destroy(bson_value_t *value); - - -BSON_END_DECLS - - -#endif /* BSON_VALUE_H */ diff --git a/bsonjs/bson/bson-vector-private.h b/bsonjs/bson/bson-vector-private.h deleted file mode 100644 index 100da3e..0000000 --- a/bsonjs/bson/bson-vector-private.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#ifndef BSON_VECTOR_PRIVATE_H -#define BSON_VECTOR_PRIVATE_H - -#include // IWYU pragma: export - -// - -#include -#include - -BSON_BEGIN_DECLS - - -typedef enum { - BSON_VECTOR_ELEMENT_SIGNED_INT = 0, - BSON_VECTOR_ELEMENT_UNSIGNED_INT = 1, - BSON_VECTOR_ELEMENT_FLOAT = 2, -} bson_vector_element_type_t; - -typedef enum { - BSON_VECTOR_ELEMENT_1_BIT = 0, - BSON_VECTOR_ELEMENT_8_BITS = 3, - BSON_VECTOR_ELEMENT_32_BITS = 7, -} bson_vector_element_size_t; - - -static BSON_INLINE uint8_t -bson_vector_header_byte_0(bson_vector_element_type_t element_type, bson_vector_element_size_t element_size) -{ - BSON_ASSERT((unsigned)element_type <= 0x0f); - BSON_ASSERT((unsigned)element_size <= 0x0f); - return (uint8_t)(((unsigned)element_type << 4) | (unsigned)element_size); -} - -// See also `bson_vector_padding_from_header_byte_1` defined in for use by public inline functions. -static BSON_INLINE uint8_t -bson_vector_header_byte_1(size_t padding) -{ - BSON_ASSERT(padding <= 7); - return (uint8_t)padding; -} - - -BSON_END_DECLS - -#endif /* BSON_VECTOR_PRIVATE_H */ diff --git a/bsonjs/bson/bson-vector.c b/bsonjs/bson/bson-vector.c deleted file mode 100644 index b934b30..0000000 --- a/bsonjs/bson/bson-vector.c +++ /dev/null @@ -1,686 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include - -#include - -#include - - -static BSON_INLINE bool -bson_vector_binary_header_impl_init(bson_vector_binary_header_impl_t *header_out, - const uint8_t *binary_data, - uint32_t binary_data_len) -{ - if (binary_data_len >= BSON_VECTOR_HEADER_LEN) { - memcpy(header_out->bytes, binary_data, BSON_VECTOR_HEADER_LEN); - return true; - } else { - return false; - } -} - -static BSON_INLINE bool -bson_vector_int8_validate(bson_vector_binary_header_impl_t header) -{ - return header.bytes[0] == bson_vector_header_byte_0(BSON_VECTOR_ELEMENT_SIGNED_INT, BSON_VECTOR_ELEMENT_8_BITS) && - header.bytes[1] == bson_vector_header_byte_1(0); -} - -static BSON_INLINE bool -bson_vector_float32_validate(bson_vector_binary_header_impl_t header, uint32_t binary_data_len) -{ - return (binary_data_len - BSON_VECTOR_HEADER_LEN) % sizeof(float) == 0 && - header.bytes[0] == bson_vector_header_byte_0(BSON_VECTOR_ELEMENT_FLOAT, BSON_VECTOR_ELEMENT_32_BITS) && - header.bytes[1] == bson_vector_header_byte_1(0); -} - -static BSON_INLINE bool -bson_vector_packed_bit_validate(bson_vector_binary_header_impl_t header, - const uint8_t *binary_data, - uint32_t binary_data_len) -{ - if (header.bytes[0] == bson_vector_header_byte_0(BSON_VECTOR_ELEMENT_UNSIGNED_INT, BSON_VECTOR_ELEMENT_1_BIT)) { - size_t padding = bson_vector_padding_from_header_byte_1(header.bytes[1]); - if (header.bytes[1] != bson_vector_header_byte_1(padding)) { - return false; - } - uint32_t vector_data_len = binary_data_len - BSON_VECTOR_HEADER_LEN; - if (vector_data_len == 0) { - return padding == 0; - } else { - // We need to read the last byte of the binary block to validate that unused bits are zero. - uint8_t last_data_byte = binary_data[binary_data_len - 1]; - uint8_t mask_of_unused_bits = (uint8_t)((1u << padding) - 1u); - return (last_data_byte & mask_of_unused_bits) == 0; - } - } else { - return false; - } -} - - -bool -bson_vector_int8_view_init(bson_vector_int8_view_t *view_out, uint8_t *binary_data, uint32_t binary_data_len) -{ - BSON_OPTIONAL_PARAM(view_out); - BSON_ASSERT_PARAM(binary_data); - bson_vector_binary_header_impl_t header; - if (bson_vector_binary_header_impl_init(&header, binary_data, binary_data_len) && - bson_vector_int8_validate(header)) { - if (view_out) { - *view_out = (bson_vector_int8_view_t){ - .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; - } - return true; - } else { - return false; - } -} - -bool -bson_vector_int8_const_view_init(bson_vector_int8_const_view_t *view_out, - const uint8_t *binary_data, - uint32_t binary_data_len) -{ - BSON_OPTIONAL_PARAM(view_out); - BSON_ASSERT_PARAM(binary_data); - bson_vector_binary_header_impl_t header; - if (bson_vector_binary_header_impl_init(&header, binary_data, binary_data_len) && - bson_vector_int8_validate(header)) { - if (view_out) { - *view_out = (bson_vector_int8_const_view_t){ - .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; - } - return true; - } else { - return false; - } -} - -bool -bson_vector_float32_view_init(bson_vector_float32_view_t *view_out, uint8_t *binary_data, uint32_t binary_data_len) -{ - BSON_OPTIONAL_PARAM(view_out); - BSON_ASSERT_PARAM(binary_data); - bson_vector_binary_header_impl_t header; - if (bson_vector_binary_header_impl_init(&header, binary_data, binary_data_len) && - bson_vector_float32_validate(header, binary_data_len)) { - if (view_out) { - *view_out = (bson_vector_float32_view_t){ - .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; - } - return true; - } else { - return false; - } -} - -bool -bson_vector_float32_const_view_init(bson_vector_float32_const_view_t *view_out, - const uint8_t *binary_data, - uint32_t binary_data_len) -{ - BSON_OPTIONAL_PARAM(view_out); - BSON_ASSERT_PARAM(binary_data); - bson_vector_binary_header_impl_t header; - if (bson_vector_binary_header_impl_init(&header, binary_data, binary_data_len) && - bson_vector_float32_validate(header, binary_data_len)) { - if (view_out) { - *view_out = (bson_vector_float32_const_view_t){ - .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; - } - return true; - } else { - return false; - } -} - -bool -bson_vector_packed_bit_view_init(bson_vector_packed_bit_view_t *view_out, - uint8_t *binary_data, - uint32_t binary_data_len) -{ - BSON_OPTIONAL_PARAM(view_out); - BSON_ASSERT_PARAM(binary_data); - bson_vector_binary_header_impl_t header; - if (bson_vector_binary_header_impl_init(&header, binary_data, binary_data_len) && - bson_vector_packed_bit_validate(header, binary_data, binary_data_len)) { - if (view_out) { - *view_out = (bson_vector_packed_bit_view_t){ - .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; - } - return true; - } else { - return false; - } -} - -bool -bson_vector_packed_bit_const_view_init(bson_vector_packed_bit_const_view_t *view_out, - const uint8_t *binary_data, - uint32_t binary_data_len) -{ - BSON_OPTIONAL_PARAM(view_out); - BSON_ASSERT_PARAM(binary_data); - bson_vector_binary_header_impl_t header; - if (bson_vector_binary_header_impl_init(&header, binary_data, binary_data_len) && - bson_vector_packed_bit_validate(header, binary_data, binary_data_len)) { - if (view_out) { - *view_out = (bson_vector_packed_bit_const_view_t){ - .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; - } - return true; - } else { - return false; - } -} - - -bool -bson_vector_int8_view_from_iter(bson_vector_int8_view_t *view_out, bson_iter_t *iter) -{ - BSON_OPTIONAL_PARAM(view_out); - BSON_ASSERT_PARAM(iter); - if (BSON_ITER_HOLDS_BINARY(iter)) { - uint32_t binary_len; - uint8_t *binary; - bson_iter_overwrite_binary(iter, BSON_SUBTYPE_VECTOR, &binary_len, &binary); - return binary && bson_vector_int8_view_init(view_out, binary, binary_len); - } else { - return false; - } -} - -bool -bson_vector_int8_const_view_from_iter(bson_vector_int8_const_view_t *view_out, const bson_iter_t *iter) -{ - BSON_OPTIONAL_PARAM(view_out); - BSON_ASSERT_PARAM(iter); - if (BSON_ITER_HOLDS_BINARY(iter)) { - bson_subtype_t subtype; - uint32_t binary_len; - const uint8_t *binary; - bson_iter_binary(iter, &subtype, &binary_len, &binary); - return binary && subtype == BSON_SUBTYPE_VECTOR && bson_vector_int8_const_view_init(view_out, binary, binary_len); - } else { - return false; - } -} - -bool -bson_vector_float32_view_from_iter(bson_vector_float32_view_t *view_out, bson_iter_t *iter) -{ - BSON_OPTIONAL_PARAM(view_out); - BSON_ASSERT_PARAM(iter); - if (BSON_ITER_HOLDS_BINARY(iter)) { - uint32_t binary_len; - uint8_t *binary; - bson_iter_overwrite_binary(iter, BSON_SUBTYPE_VECTOR, &binary_len, &binary); - return binary && bson_vector_float32_view_init(view_out, binary, binary_len); - } else { - return false; - } -} - -bool -bson_vector_float32_const_view_from_iter(bson_vector_float32_const_view_t *view_out, const bson_iter_t *iter) -{ - BSON_OPTIONAL_PARAM(view_out); - BSON_ASSERT_PARAM(iter); - if (BSON_ITER_HOLDS_BINARY(iter)) { - bson_subtype_t subtype; - uint32_t binary_len; - const uint8_t *binary; - bson_iter_binary(iter, &subtype, &binary_len, &binary); - return binary && subtype == BSON_SUBTYPE_VECTOR && - bson_vector_float32_const_view_init(view_out, binary, binary_len); - } else { - return false; - } -} - -bool -bson_vector_packed_bit_view_from_iter(bson_vector_packed_bit_view_t *view_out, bson_iter_t *iter) -{ - BSON_OPTIONAL_PARAM(view_out); - BSON_ASSERT_PARAM(iter); - if (BSON_ITER_HOLDS_BINARY(iter)) { - uint32_t binary_len; - uint8_t *binary; - bson_iter_overwrite_binary(iter, BSON_SUBTYPE_VECTOR, &binary_len, &binary); - return binary && bson_vector_packed_bit_view_init(view_out, binary, binary_len); - } else { - return false; - } -} - -bool -bson_vector_packed_bit_const_view_from_iter(bson_vector_packed_bit_const_view_t *view_out, const bson_iter_t *iter) -{ - BSON_OPTIONAL_PARAM(view_out); - BSON_ASSERT_PARAM(iter); - if (BSON_ITER_HOLDS_BINARY(iter)) { - bson_subtype_t subtype; - uint32_t binary_len; - const uint8_t *binary; - bson_iter_binary(iter, &subtype, &binary_len, &binary); - return binary && subtype == BSON_SUBTYPE_VECTOR && - bson_vector_packed_bit_const_view_init(view_out, binary, binary_len); - } else { - return false; - } -} - - -bool -bson_append_vector_int8_uninit( - bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_int8_view_t *view_out) -{ - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(view_out); - - uint32_t length = bson_vector_int8_binary_data_length(element_count); - if (length < BSON_VECTOR_HEADER_LEN) { - return false; - } - uint8_t *binary; - if (bson_append_binary_uninit(bson, key, key_length, BSON_SUBTYPE_VECTOR, &binary, length)) { - bson_vector_binary_header_impl_t header = { - .bytes[0] = bson_vector_header_byte_0(BSON_VECTOR_ELEMENT_SIGNED_INT, BSON_VECTOR_ELEMENT_8_BITS), - .bytes[1] = bson_vector_header_byte_1(0)}; - memcpy(binary, header.bytes, BSON_VECTOR_HEADER_LEN); - *view_out = - (bson_vector_int8_view_t){.binary.data = binary, .binary.data_len = length, .binary.header_copy = header}; - return true; - } else { - return false; - } -} - -bool -bson_append_vector_float32_uninit( - bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_float32_view_t *view_out) -{ - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(view_out); - - uint32_t length = bson_vector_float32_binary_data_length(element_count); - if (length < BSON_VECTOR_HEADER_LEN) { - return false; - } - uint8_t *binary; - if (bson_append_binary_uninit(bson, key, key_length, BSON_SUBTYPE_VECTOR, &binary, length)) { - bson_vector_binary_header_impl_t header = { - .bytes[0] = bson_vector_header_byte_0(BSON_VECTOR_ELEMENT_FLOAT, BSON_VECTOR_ELEMENT_32_BITS), - .bytes[1] = bson_vector_header_byte_1(0)}; - memcpy(binary, header.bytes, BSON_VECTOR_HEADER_LEN); - *view_out = - (bson_vector_float32_view_t){.binary.data = binary, .binary.data_len = length, .binary.header_copy = header}; - return true; - } else { - return false; - } -} - -bool -bson_append_vector_packed_bit_uninit( - bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_packed_bit_view_t *view_out) -{ - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(view_out); - - uint32_t length = bson_vector_packed_bit_binary_data_length(element_count); - if (length < BSON_VECTOR_HEADER_LEN) { - return false; - } - uint8_t *binary; - if (bson_append_binary_uninit(bson, key, key_length, BSON_SUBTYPE_VECTOR, &binary, length)) { - mlib_diagnostic_push(); - mlib_msvc_warning(disable : 4146); - size_t padding = (size_t)7 & -element_count; - mlib_diagnostic_pop(); - - bson_vector_binary_header_impl_t header = { - .bytes[0] = bson_vector_header_byte_0(BSON_VECTOR_ELEMENT_UNSIGNED_INT, BSON_VECTOR_ELEMENT_1_BIT), - .bytes[1] = bson_vector_header_byte_1(padding)}; - memcpy(binary, header.bytes, BSON_VECTOR_HEADER_LEN); - if (element_count > 0 && padding > 0) { - // We must explicitly zero bits in the final byte that aren't part of any element. - // No reason to read-modify-write here, it's better to write the whole byte. - binary[length - 1u] = 0u; - } - *view_out = (bson_vector_packed_bit_view_t){ - .binary.data = binary, .binary.data_len = length, .binary.header_copy = header}; - return true; - } else { - return false; - } -} - -static bool -bson_vector_from_array_expect_key(const bson_iter_t *iter, uint32_t numeric_key, bson_error_t *error) -{ - char buffer[16]; - const char *key; - bson_uint32_to_string(numeric_key, &key, buffer, sizeof buffer); - if (0 == strcmp(key, bson_iter_key(iter))) { - return true; - } else { - bson_set_error(error, - BSON_ERROR_VECTOR, - BSON_VECTOR_ERROR_ARRAY_KEY, - "expected BSON array key '%s', found key '%s'", - key, - bson_iter_key(iter)); - return false; - } -} - -static void -bson_vector_set_error_max_size(bson_error_t *error) -{ - bson_set_error(error, BSON_ERROR_VECTOR, BSON_VECTOR_ERROR_MAX_SIZE, "maximum BSON document size would be exceeded"); -} - -bool -bson_append_vector_int8_from_array( - bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error) -{ - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - BSON_ASSERT_PARAM(iter); - - uint32_t element_count = 0; - { - bson_iter_t validation_iter = *iter; - while (bson_iter_next(&validation_iter)) { - if (!bson_vector_from_array_expect_key(&validation_iter, element_count, error)) { - return false; - } - if (!BSON_ITER_HOLDS_INT(&validation_iter)) { - bson_set_error(error, - BSON_ERROR_VECTOR, - BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE, - "expected int32 or int64 in BSON array key '%s', found item type 0x%02X", - bson_iter_key(&validation_iter), - (unsigned)bson_iter_type(&validation_iter)); - return false; - } - int64_t element_as_int64 = bson_iter_as_int64(&validation_iter); - if (element_as_int64 < INT8_MIN || element_as_int64 > INT8_MAX) { - bson_set_error(error, - BSON_ERROR_VECTOR, - BSON_VECTOR_ERROR_ARRAY_ELEMENT_VALUE, - "BSON array key '%s' value %" PRId64 " is out of range for vector of int8", - bson_iter_key(&validation_iter), - element_as_int64); - return false; - } - element_count++; - } - } - - bson_vector_int8_view_t view; - if (!bson_append_vector_int8_uninit(bson, key, key_length, element_count, &view)) { - bson_vector_set_error_max_size(error); - return false; - } - bson_iter_t copy_iter = *iter; - for (uint32_t i = 0; i < element_count; i++) { - BSON_ASSERT(bson_iter_next(©_iter)); - int8_t element = (int8_t)bson_iter_as_int64(©_iter); - BSON_ASSERT(bson_vector_int8_view_write(view, &element, 1, i)); - } - return true; -} - -bool -bson_append_vector_float32_from_array( - bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error) -{ - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - BSON_ASSERT_PARAM(iter); - - uint32_t element_count = 0; - { - bson_iter_t validation_iter = *iter; - while (bson_iter_next(&validation_iter)) { - if (!bson_vector_from_array_expect_key(&validation_iter, element_count, error)) { - return false; - } - if (!BSON_ITER_HOLDS_DOUBLE(&validation_iter)) { - bson_set_error(error, - BSON_ERROR_VECTOR, - BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE, - "expected 'double' number type in BSON array key '%s', found item type 0x%02X", - bson_iter_key(&validation_iter), - (unsigned)bson_iter_type(&validation_iter)); - return false; - } - element_count++; - } - } - - bson_vector_float32_view_t view; - if (!bson_append_vector_float32_uninit(bson, key, key_length, element_count, &view)) { - bson_vector_set_error_max_size(error); - return false; - } - bson_iter_t copy_iter = *iter; - for (uint32_t i = 0; i < element_count; i++) { - BSON_ASSERT(bson_iter_next(©_iter)); - float element = (float)bson_iter_double(©_iter); - BSON_ASSERT(bson_vector_float32_view_write(view, &element, 1, i)); - } - return true; -} - -bool -bson_append_vector_packed_bit_from_array( - bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error) -{ - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - BSON_ASSERT_PARAM(iter); - - uint32_t element_count = 0; - { - bson_iter_t validation_iter = *iter; - while (bson_iter_next(&validation_iter)) { - if (!bson_vector_from_array_expect_key(&validation_iter, element_count, error)) { - return false; - } - if (!BSON_ITER_HOLDS_INT(&validation_iter) && !BSON_ITER_HOLDS_BOOL(&validation_iter)) { - bson_set_error(error, - BSON_ERROR_VECTOR, - BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE, - "expected int32, int64, or bool in BSON array key '%s', found item type 0x%02X", - bson_iter_key(&validation_iter), - (unsigned)bson_iter_type(&validation_iter)); - return false; - } - int64_t element_as_int64 = bson_iter_as_int64(&validation_iter); - if (element_as_int64 < 0 || element_as_int64 > 1) { - bson_set_error(error, - BSON_ERROR_VECTOR, - BSON_VECTOR_ERROR_ARRAY_ELEMENT_VALUE, - "BSON array key '%s' value %" PRId64 " is out of range for vector of packed_bit", - bson_iter_key(&validation_iter), - element_as_int64); - return false; - } - element_count++; - } - } - - bson_vector_packed_bit_view_t view; - if (!bson_append_vector_packed_bit_uninit(bson, key, key_length, element_count, &view)) { - bson_vector_set_error_max_size(error); - return false; - } - bson_iter_t copy_iter = *iter; - for (uint32_t i = 0; i < element_count; i++) { - BSON_ASSERT(bson_iter_next(©_iter)); - bool element_as_bool = (bool)bson_iter_as_int64(©_iter); - BSON_ASSERT(bson_vector_packed_bit_view_pack_bool(view, &element_as_bool, 1, i)); - } - return true; -} - - -bool -bson_array_builder_append_vector_int8_elements(bson_array_builder_t *builder, bson_vector_int8_const_view_t view) -{ - BSON_ASSERT_PARAM(builder); - size_t length = bson_vector_int8_const_view_length(view); - for (size_t i = 0; i < length; i++) { - // Note, the zero initializer is only needed due to a false positive -Wmaybe-uninitialized warning in uncommon - // configurations where the compiler does not have visibility into memcpy(). - int8_t element = 0; - BSON_ASSERT(bson_vector_int8_const_view_read(view, &element, 1, i)); - if (!bson_array_builder_append_int32(builder, (int32_t)element)) { - return false; - } - } - return true; -} - -bool -bson_array_builder_append_vector_float32_elements(bson_array_builder_t *builder, bson_vector_float32_const_view_t view) -{ - BSON_ASSERT_PARAM(builder); - size_t length = bson_vector_float32_const_view_length(view); - for (size_t i = 0; i < length; i++) { - float element; - BSON_ASSERT(bson_vector_float32_const_view_read(view, &element, 1, i)); - if (!bson_array_builder_append_double(builder, (double)element)) { - return false; - } - } - return true; -} - -bool -bson_array_builder_append_vector_packed_bit_elements(bson_array_builder_t *builder, - bson_vector_packed_bit_const_view_t view) -{ - BSON_ASSERT_PARAM(builder); - size_t length = bson_vector_packed_bit_const_view_length(view); - for (size_t i = 0; i < length; i++) { - bool element; - BSON_ASSERT(bson_vector_packed_bit_const_view_unpack_bool(view, &element, 1, i)); - if (!bson_array_builder_append_int32(builder, element ? 1 : 0)) { - return false; - } - } - return true; -} - - -bool -bson_array_builder_append_vector_elements(bson_array_builder_t *builder, const bson_iter_t *iter) -{ - BSON_ASSERT_PARAM(builder); - BSON_ASSERT_PARAM(iter); - { - bson_vector_int8_const_view_t view; - if (bson_vector_int8_const_view_from_iter(&view, iter)) { - return bson_array_builder_append_vector_int8_elements(builder, view); - } - } - { - bson_vector_float32_const_view_t view; - if (bson_vector_float32_const_view_from_iter(&view, iter)) { - return bson_array_builder_append_vector_float32_elements(builder, view); - } - } - { - bson_vector_packed_bit_const_view_t view; - if (bson_vector_packed_bit_const_view_from_iter(&view, iter)) { - return bson_array_builder_append_vector_packed_bit_elements(builder, view); - } - } - return false; -} - - -bool -bson_append_array_from_vector_int8(bson_t *bson, const char *key, int key_length, bson_vector_int8_const_view_t view) -{ - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - bson_array_builder_t *child; - if (bson_append_array_builder_begin(bson, key, key_length, &child)) { - bool ok = bson_array_builder_append_vector_int8_elements(child, view); - return bson_append_array_builder_end(bson, child) && ok; - } else { - return false; - } -} - -bool -bson_append_array_from_vector_float32(bson_t *bson, - const char *key, - int key_length, - bson_vector_float32_const_view_t view) -{ - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - bson_array_builder_t *child; - if (bson_append_array_builder_begin(bson, key, key_length, &child)) { - bool ok = bson_array_builder_append_vector_float32_elements(child, view); - return bson_append_array_builder_end(bson, child) && ok; - } else { - return false; - } -} - -bool -bson_append_array_from_vector_packed_bit(bson_t *bson, - const char *key, - int key_length, - bson_vector_packed_bit_const_view_t view) -{ - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - bson_array_builder_t *child; - if (bson_append_array_builder_begin(bson, key, key_length, &child)) { - bool ok = bson_array_builder_append_vector_packed_bit_elements(child, view); - return bson_append_array_builder_end(bson, child) && ok; - } else { - return false; - } -} - - -bool -bson_append_array_from_vector(bson_t *bson, const char *key, int key_length, const bson_iter_t *iter) -{ - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - BSON_ASSERT_PARAM(iter); - bson_array_builder_t *child; - if (bson_append_array_builder_begin(bson, key, key_length, &child)) { - bool ok = bson_array_builder_append_vector_elements(child, iter); - return bson_append_array_builder_end(bson, child) && ok; - } else { - return false; - } -} diff --git a/bsonjs/bson/bson-vector.h b/bsonjs/bson/bson-vector.h deleted file mode 100644 index f58c982..0000000 --- a/bsonjs/bson/bson-vector.h +++ /dev/null @@ -1,615 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#ifndef BSON_VECTOR_H -#define BSON_VECTOR_H - -#include -#include -#include -#include -#include - -BSON_BEGIN_DECLS - - -// Length of the required header for BSON_SUBTYPE_VECTOR, in bytes -#define BSON_VECTOR_HEADER_LEN 2 - -// Forward declaration (typedef bson_array_builder_t in bson.h) -struct _bson_array_builder_t; - -/** @brief Error codes for domain BSON_ERROR_VECTOR */ -typedef enum { - BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE = 1, - BSON_VECTOR_ERROR_ARRAY_ELEMENT_VALUE, - BSON_VECTOR_ERROR_ARRAY_KEY, - BSON_VECTOR_ERROR_MAX_SIZE, -} bson_vector_error_code_t; - - -/** @brief Implementation detail. A copy of the BSON_SUBTYPE_VECTOR header, suitable for pass-by-value. */ -typedef struct bson_vector_binary_header_impl_t { - uint8_t bytes[BSON_VECTOR_HEADER_LEN]; -} bson_vector_binary_header_impl_t; - -/** @brief Implementation detail. A reference to non-owned const BSON Binary data of subtype BSON_SUBTYPE_VECTOR */ -typedef struct bson_vector_binary_const_view_impl_t { - const uint8_t *data; - uint32_t data_len; - bson_vector_binary_header_impl_t header_copy; -} bson_vector_binary_const_view_impl_t; - -/** @brief Implementation detail. A reference to non-owned BSON Binary data of subtype BSON_SUBTYPE_VECTOR */ -typedef struct bson_vector_binary_view_impl_t { - uint8_t *data; - uint32_t data_len; - bson_vector_binary_header_impl_t header_copy; -} bson_vector_binary_view_impl_t; - -/** @brief Implementation detail. Obtain a const reference from a non-const reference without re-validating. */ -static BSON_INLINE bson_vector_binary_const_view_impl_t -bson_vector_binary_view_impl_as_const(bson_vector_binary_view_impl_t view) -{ - bson_vector_binary_const_view_impl_t result; - result.data = view.data; - result.data_len = view.data_len; - result.header_copy = view.header_copy; - return result; -} - - -/** @brief A reference to non-owned BSON Binary data holding a valid Vector of int8 element type */ -typedef struct bson_vector_int8_view_t { - bson_vector_binary_view_impl_t binary; -} bson_vector_int8_view_t; - -/** @brief A reference to non-owned const BSON Binary data holding a valid Vector of int8 element type */ -typedef struct bson_vector_int8_const_view_t { - bson_vector_binary_const_view_impl_t binary; -} bson_vector_int8_const_view_t; - -/** @brief A reference to non-owned BSON Binary data holding a valid Vector of float32 element type */ -typedef struct bson_vector_float32_view_t { - bson_vector_binary_view_impl_t binary; -} bson_vector_float32_view_t; - -/** @brief A reference to non-owned const BSON Binary data holding a valid Vector of float32 element type */ -typedef struct bson_vector_float32_const_view_t { - bson_vector_binary_const_view_impl_t binary; -} bson_vector_float32_const_view_t; - -/** @brief A reference to non-owned BSON Binary data holding a valid Vector of packed_bit */ -typedef struct bson_vector_packed_bit_view_t { - bson_vector_binary_view_impl_t binary; -} bson_vector_packed_bit_view_t; - -/** @brief A reference to non-owned const BSON Binary data holding a valid Vector of packed_bit */ -typedef struct bson_vector_packed_bit_const_view_t { - bson_vector_binary_const_view_impl_t binary; -} bson_vector_packed_bit_const_view_t; - - -static BSON_INLINE bson_vector_int8_const_view_t -bson_vector_int8_view_as_const(bson_vector_int8_view_t view) -{ - bson_vector_int8_const_view_t result; - result.binary = bson_vector_binary_view_impl_as_const(view.binary); - return result; -} - -static BSON_INLINE bson_vector_float32_const_view_t -bson_vector_float32_view_as_const(bson_vector_float32_view_t view) -{ - bson_vector_float32_const_view_t result; - result.binary = bson_vector_binary_view_impl_as_const(view.binary); - return result; -} - -static BSON_INLINE bson_vector_packed_bit_const_view_t -bson_vector_packed_bit_view_as_const(bson_vector_packed_bit_view_t view) -{ - bson_vector_packed_bit_const_view_t result; - result.binary = bson_vector_binary_view_impl_as_const(view.binary); - return result; -} - - -BSON_EXPORT(bool) -bson_vector_int8_view_init(bson_vector_int8_view_t *view_out, uint8_t *binary_data, uint32_t binary_data_len); - -BSON_EXPORT(bool) -bson_vector_int8_const_view_init(bson_vector_int8_const_view_t *view_out, - const uint8_t *binary_data, - uint32_t binary_data_len); - -BSON_EXPORT(bool) -bson_vector_float32_view_init(bson_vector_float32_view_t *view_out, uint8_t *binary_data, uint32_t binary_data_len); - - -BSON_EXPORT(bool) -bson_vector_float32_const_view_init(bson_vector_float32_const_view_t *view_out, - const uint8_t *binary_data, - uint32_t binary_data_len); - -BSON_EXPORT(bool) -bson_vector_packed_bit_view_init(bson_vector_packed_bit_view_t *view_out, - uint8_t *binary_data, - uint32_t binary_data_len); - -BSON_EXPORT(bool) -bson_vector_packed_bit_const_view_init(bson_vector_packed_bit_const_view_t *view_out, - const uint8_t *binary_data, - uint32_t binary_data_len); - - -BSON_EXPORT(bool) -bson_vector_int8_view_from_iter(bson_vector_int8_view_t *view_out, bson_iter_t *iter); - -BSON_EXPORT(bool) -bson_vector_int8_const_view_from_iter(bson_vector_int8_const_view_t *view_out, const bson_iter_t *iter); - -BSON_EXPORT(bool) -bson_vector_float32_view_from_iter(bson_vector_float32_view_t *view_out, bson_iter_t *iter); - -BSON_EXPORT(bool) -bson_vector_float32_const_view_from_iter(bson_vector_float32_const_view_t *view_out, const bson_iter_t *iter); - -BSON_EXPORT(bool) -bson_vector_packed_bit_view_from_iter(bson_vector_packed_bit_view_t *view_out, bson_iter_t *iter); - -BSON_EXPORT(bool) -bson_vector_packed_bit_const_view_from_iter(bson_vector_packed_bit_const_view_t *view_out, const bson_iter_t *iter); - - -BSON_EXPORT(bool) -bson_array_builder_append_vector_int8_elements(struct _bson_array_builder_t *builder, - bson_vector_int8_const_view_t view); - -BSON_EXPORT(bool) -bson_array_builder_append_vector_float32_elements(struct _bson_array_builder_t *builder, - bson_vector_float32_const_view_t view); - -BSON_EXPORT(bool) -bson_array_builder_append_vector_packed_bit_elements(struct _bson_array_builder_t *builder, - bson_vector_packed_bit_const_view_t view); - -BSON_EXPORT(bool) -bson_array_builder_append_vector_elements(struct _bson_array_builder_t *builder, const bson_iter_t *iter); - - -BSON_EXPORT(bool) -bson_append_vector_int8_uninit( - bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_int8_view_t *view_out); - -#define BSON_APPEND_VECTOR_INT8_UNINIT(b, key, count, view) \ - bson_append_vector_int8_uninit(b, key, (int)strlen(key), count, view) - -BSON_EXPORT(bool) -bson_append_vector_float32_uninit( - bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_float32_view_t *view_out); - -#define BSON_APPEND_VECTOR_FLOAT32_UNINIT(b, key, count, view) \ - bson_append_vector_float32_uninit(b, key, (int)strlen(key), count, view) - -BSON_EXPORT(bool) -bson_append_vector_packed_bit_uninit( - bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_packed_bit_view_t *view_out); - -#define BSON_APPEND_VECTOR_PACKED_BIT_UNINIT(b, key, count, view) \ - bson_append_vector_packed_bit_uninit(b, key, (int)strlen(key), count, view) - - -BSON_EXPORT(bool) -bson_append_vector_int8_from_array( - bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error); - -#define BSON_APPEND_VECTOR_INT8_FROM_ARRAY(b, key, iter, err) \ - bson_append_vector_int8_from_array(b, key, (int)strlen(key), iter, err) - -BSON_EXPORT(bool) -bson_append_vector_float32_from_array( - bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error); - -#define BSON_APPEND_VECTOR_FLOAT32_FROM_ARRAY(b, key, iter, err) \ - bson_append_vector_float32_from_array(b, key, (int)strlen(key), iter, err) - -BSON_EXPORT(bool) -bson_append_vector_packed_bit_from_array( - bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error); - -#define BSON_APPEND_VECTOR_PACKED_BIT_FROM_ARRAY(b, key, iter, err) \ - bson_append_vector_packed_bit_from_array(b, key, (int)strlen(key), iter, err) - - -BSON_EXPORT(bool) -bson_append_array_from_vector_int8(bson_t *bson, const char *key, int key_length, bson_vector_int8_const_view_t view); - -#define BSON_APPEND_ARRAY_FROM_VECTOR_INT8(b, key, view) \ - bson_append_array_from_vector_int8(b, key, (int)strlen(key), view) - -BSON_EXPORT(bool) -bson_append_array_from_vector_float32(bson_t *bson, - const char *key, - int key_length, - bson_vector_float32_const_view_t view); - -#define BSON_APPEND_ARRAY_FROM_VECTOR_FLOAT32(b, key, view) \ - bson_append_array_from_vector_float32(b, key, (int)strlen(key), view) - -BSON_EXPORT(bool) -bson_append_array_from_vector_packed_bit(bson_t *bson, - const char *key, - int key_length, - bson_vector_packed_bit_const_view_t view); - -#define BSON_APPEND_ARRAY_FROM_VECTOR_PACKED_BIT(b, key, view) \ - bson_append_array_from_vector_packed_bit(b, key, (int)strlen(key), view) - - -static BSON_INLINE const int8_t * -bson_vector_int8_const_view_pointer(bson_vector_int8_const_view_t view) -{ - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - return (const int8_t *)(view.binary.data + BSON_VECTOR_HEADER_LEN); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END -} - -static BSON_INLINE int8_t * -bson_vector_int8_view_pointer(bson_vector_int8_view_t view) -{ - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - return (int8_t *)(view.binary.data + BSON_VECTOR_HEADER_LEN); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END -} - - -static BSON_INLINE uint32_t -bson_vector_int8_binary_data_length(size_t element_count) -{ - const size_t max_representable = (size_t)UINT32_MAX - (size_t)BSON_VECTOR_HEADER_LEN; - return element_count > max_representable ? 0u : (uint32_t)element_count + (uint32_t)BSON_VECTOR_HEADER_LEN; -} - -static BSON_INLINE uint32_t -bson_vector_float32_binary_data_length(size_t element_count) -{ - const size_t max_representable = ((size_t)UINT32_MAX - (size_t)BSON_VECTOR_HEADER_LEN) / sizeof(float); - return element_count > max_representable - ? 0u - : (uint32_t)element_count * sizeof(float) + (uint32_t)BSON_VECTOR_HEADER_LEN; -} - -static BSON_INLINE uint32_t -bson_vector_packed_bit_binary_data_length(size_t element_count) -{ - const size_t max_representable = - (size_t)BSON_MIN((uint64_t)SIZE_MAX, ((uint64_t)UINT32_MAX - (uint64_t)BSON_VECTOR_HEADER_LEN) * 8u); - return element_count > max_representable - ? 0u - : (uint32_t)(((uint64_t)element_count + 7u) / 8u) + (uint32_t)BSON_VECTOR_HEADER_LEN; -} - - -static BSON_INLINE size_t -bson_vector_int8_const_view_length(bson_vector_int8_const_view_t view) -{ - return view.binary.data_len - (uint32_t)BSON_VECTOR_HEADER_LEN; -} - -static BSON_INLINE size_t -bson_vector_int8_view_length(bson_vector_int8_view_t view) -{ - return bson_vector_int8_const_view_length(bson_vector_int8_view_as_const(view)); -} - -static BSON_INLINE size_t -bson_vector_float32_const_view_length(bson_vector_float32_const_view_t view) -{ - return (view.binary.data_len - (uint32_t)BSON_VECTOR_HEADER_LEN) / (uint32_t)sizeof(float); -} - -static BSON_INLINE size_t -bson_vector_float32_view_length(bson_vector_float32_view_t view) -{ - return bson_vector_float32_const_view_length(bson_vector_float32_view_as_const(view)); -} - -static BSON_INLINE size_t -bson_vector_packed_bit_const_view_length_bytes(bson_vector_packed_bit_const_view_t view) -{ - return view.binary.data_len - (uint32_t)BSON_VECTOR_HEADER_LEN; -} - -static BSON_INLINE size_t -bson_vector_packed_bit_view_length_bytes(bson_vector_packed_bit_view_t view) -{ - return bson_vector_packed_bit_const_view_length_bytes(bson_vector_packed_bit_view_as_const(view)); -} - -// Implementation detail, not part of documented API. -static BSON_INLINE size_t -bson_vector_padding_from_header_byte_1(uint8_t byte_1) -{ - return byte_1 & 7; -} - -static BSON_INLINE size_t -bson_vector_packed_bit_const_view_padding(bson_vector_packed_bit_const_view_t view) -{ - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - return bson_vector_padding_from_header_byte_1(view.binary.header_copy.bytes[1]); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END -} - -static BSON_INLINE size_t -bson_vector_packed_bit_view_padding(bson_vector_packed_bit_view_t view) -{ - return bson_vector_packed_bit_const_view_padding(bson_vector_packed_bit_view_as_const(view)); -} - -static BSON_INLINE size_t -bson_vector_packed_bit_const_view_length(bson_vector_packed_bit_const_view_t view) -{ - return bson_vector_packed_bit_const_view_length_bytes(view) * 8u - bson_vector_packed_bit_const_view_padding(view); -} - -static BSON_INLINE size_t -bson_vector_packed_bit_view_length(bson_vector_packed_bit_view_t view) -{ - return bson_vector_packed_bit_const_view_length(bson_vector_packed_bit_view_as_const(view)); -} - - -static BSON_INLINE bool -bson_vector_int8_const_view_read(bson_vector_int8_const_view_t view, - int8_t *BSON_RESTRICT values_out, - size_t element_count, - size_t vector_offset_elements) -{ - size_t length = bson_vector_int8_const_view_length(view); - if (BSON_LIKELY(vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - memcpy(values_out, bson_vector_int8_const_view_pointer(view) + vector_offset_elements, element_count); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - return true; - } else { - return false; - } -} - -static BSON_INLINE bool -bson_vector_int8_view_read(bson_vector_int8_view_t view, - int8_t *BSON_RESTRICT values_out, - size_t element_count, - uint32_t vector_offset_elements) -{ - return bson_vector_int8_const_view_read( - bson_vector_int8_view_as_const(view), values_out, element_count, vector_offset_elements); -} - -static BSON_INLINE bool -bson_vector_int8_view_write(bson_vector_int8_view_t view, - const int8_t *BSON_RESTRICT values, - size_t element_count, - size_t vector_offset_elements) -{ - size_t length = bson_vector_int8_view_length(view); - if (BSON_LIKELY(vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - memcpy(bson_vector_int8_view_pointer(view) + vector_offset_elements, values, element_count); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - return true; - } else { - return false; - } -} - - -BSON_STATIC_ASSERT2(float_is_float32, sizeof(float) == 4); - -static BSON_INLINE bool -bson_vector_float32_const_view_read(bson_vector_float32_const_view_t view, - float *BSON_RESTRICT values_out, - size_t element_count, - size_t vector_offset_elements) -{ - size_t length = bson_vector_float32_const_view_length(view); - if (BSON_LIKELY(vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { - size_t byte_offset = BSON_VECTOR_HEADER_LEN + vector_offset_elements * 4; - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN -#if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN - memcpy(values_out, view.binary.data + byte_offset, element_count * 4); -#else - size_t i; - for (i = 0; i < element_count; i++) { - float aligned_tmp; - memcpy(&aligned_tmp, view.binary.data + byte_offset + i * 4, 4); - values_out[i] = BSON_FLOAT_FROM_LE(aligned_tmp); - } -#endif - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - return true; - } else { - return false; - } -} - -static BSON_INLINE bool -bson_vector_float32_view_read(bson_vector_float32_view_t view, - float *BSON_RESTRICT values_out, - size_t element_count, - size_t vector_offset_elements) -{ - return bson_vector_float32_const_view_read( - bson_vector_float32_view_as_const(view), values_out, element_count, vector_offset_elements); -} - -static BSON_INLINE bool -bson_vector_float32_view_write(bson_vector_float32_view_t view, - const float *BSON_RESTRICT values, - size_t element_count, - size_t vector_offset_elements) -{ - size_t length = bson_vector_float32_view_length(view); - if (BSON_LIKELY(vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { - size_t byte_offset = BSON_VECTOR_HEADER_LEN + vector_offset_elements * 4; - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN -#if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN - memcpy(view.binary.data + byte_offset, values, element_count * 4); -#else - size_t i; - for (i = 0; i < element_count; i++) { - float aligned_tmp = BSON_FLOAT_TO_LE(values[i]); - memcpy(view.binary.data + byte_offset + i * 4, &aligned_tmp, 4); - } -#endif - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - return true; - } else { - return false; - } -} - - -static BSON_INLINE bool -bson_vector_packed_bit_const_view_read_packed(bson_vector_packed_bit_const_view_t view, - uint8_t *BSON_RESTRICT packed_values_out, - size_t byte_count, - size_t vector_offset_bytes) -{ - size_t length_bytes = bson_vector_packed_bit_const_view_length_bytes(view); - if (BSON_LIKELY(vector_offset_bytes <= length_bytes && byte_count <= length_bytes - vector_offset_bytes)) { - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - memcpy(packed_values_out, view.binary.data + BSON_VECTOR_HEADER_LEN + vector_offset_bytes, byte_count); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - return true; - } else { - return false; - } -} - -static BSON_INLINE bool -bson_vector_packed_bit_view_read_packed(bson_vector_packed_bit_view_t view, - uint8_t *BSON_RESTRICT packed_values_out, - size_t byte_count, - size_t vector_offset_bytes) -{ - return bson_vector_packed_bit_const_view_read_packed( - bson_vector_packed_bit_view_as_const(view), packed_values_out, byte_count, vector_offset_bytes); -} - -static BSON_INLINE bool -bson_vector_packed_bit_view_write_packed(bson_vector_packed_bit_view_t view, - const uint8_t *BSON_RESTRICT packed_values, - size_t byte_count, - size_t vector_offset_bytes) -{ - size_t length_bytes = bson_vector_packed_bit_view_length_bytes(view); - if (BSON_LIKELY(vector_offset_bytes <= length_bytes && byte_count <= length_bytes - vector_offset_bytes)) { - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - if (byte_count == length_bytes - vector_offset_bytes && byte_count >= 1u) { - // This write touches the last byte in the vector: - // special-case that byte so we can ensure unused bits remain set to zero. - size_t other_bytes = byte_count - 1u; - memcpy(view.binary.data + BSON_VECTOR_HEADER_LEN + vector_offset_bytes, packed_values, other_bytes); - view.binary.data[BSON_VECTOR_HEADER_LEN + vector_offset_bytes + other_bytes] = - (UINT8_C(0xFF) << bson_vector_packed_bit_view_padding(view)) & packed_values[other_bytes]; - } else { - memcpy(view.binary.data + BSON_VECTOR_HEADER_LEN + vector_offset_bytes, packed_values, byte_count); - } - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - return true; - } else { - return false; - } -} - - -static BSON_INLINE bool -bson_vector_packed_bit_const_view_unpack_bool(bson_vector_packed_bit_const_view_t view, - bool *BSON_RESTRICT unpacked_values_out, - size_t element_count, - size_t vector_offset_elements) -{ - size_t length = bson_vector_packed_bit_const_view_length(view); - if (BSON_LIKELY(vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { - size_t i; - for (i = 0; i < element_count; i++) { - size_t element_index = vector_offset_elements + i; - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - uint8_t packed_byte = view.binary.data[BSON_VECTOR_HEADER_LEN + (element_index >> 3)]; - unpacked_values_out[i] = 0 != (packed_byte & ((uint8_t)0x80 >> (element_index & 7))); - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - } - return true; - } else { - return false; - } -} - -static BSON_INLINE bool -bson_vector_packed_bit_view_unpack_bool(bson_vector_packed_bit_view_t view, - bool *BSON_RESTRICT unpacked_values_out, - size_t element_count, - size_t vector_offset_elements) -{ - return bson_vector_packed_bit_const_view_unpack_bool( - bson_vector_packed_bit_view_as_const(view), unpacked_values_out, element_count, vector_offset_elements); -} - -static BSON_INLINE bool -bson_vector_packed_bit_view_pack_bool(bson_vector_packed_bit_view_t view, - const bool *BSON_RESTRICT unpacked_values, - size_t element_count, - size_t vector_offset_elements) -{ - size_t length = bson_vector_packed_bit_view_length(view); - if (BSON_LIKELY(vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { - while (element_count > 0) { - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN - uint8_t *BSON_RESTRICT packed_byte = BSON_VECTOR_HEADER_LEN + (vector_offset_elements >> 3) + view.binary.data; - if (element_count >= 8 && (vector_offset_elements & 7) == 0) { - uint8_t complete_byte = 0; - unsigned i; - for (i = 0; i < 8; i++) { - complete_byte |= unpacked_values[i] ? ((uint8_t)0x80 >> i) : 0; - } - *packed_byte = complete_byte; - unpacked_values += 8; - vector_offset_elements += 8; - element_count -= 8; - } else { - uint8_t mask = (uint8_t)0x80 >> (vector_offset_elements & 7); - *packed_byte = (*packed_byte & ~mask) | (*unpacked_values ? mask : 0); - unpacked_values++; - vector_offset_elements++; - element_count--; - } - BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END - } - return true; - } else { - return false; - } -} - - -BSON_END_DECLS - -#endif /* BSON_VECTOR_H */ diff --git a/bsonjs/bson/bson-version-functions.c b/bsonjs/bson/bson-version-functions.c deleted file mode 100644 index 4131bc4..0000000 --- a/bsonjs/bson/bson-version-functions.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include - -/** - * bson_get_major_version: - * - * Helper function to return the runtime major version of the library. - */ -int -bson_get_major_version(void) -{ - return BSON_MAJOR_VERSION; -} - -/** - * bson_get_minor_version: - * - * Helper function to return the runtime minor version of the library. - */ -int -bson_get_minor_version(void) -{ - return BSON_MINOR_VERSION; -} - -/** - * bson_get_micro_version: - * - * Helper function to return the runtime micro version of the library. - */ -int -bson_get_micro_version(void) -{ - return BSON_MICRO_VERSION; -} - -/** - * bson_get_version: - * - * Helper function to return the runtime string version of the library. - */ -const char * -bson_get_version(void) -{ - return BSON_VERSION_S; -} - -/** - * bson_check_version: - * - * True if libmongoc's version is greater than or equal to the required - * version. - */ -bool -bson_check_version(int required_major, int required_minor, int required_micro) -{ - return BSON_CHECK_VERSION(required_major, required_minor, required_micro); -} diff --git a/bsonjs/bson/bson-version-functions.h b/bsonjs/bson/bson-version-functions.h deleted file mode 100644 index de22c15..0000000 --- a/bsonjs/bson/bson-version-functions.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include - - -#ifndef BSON_VERSION_FUNCTIONS_H -#define BSON_VERSION_FUNCTIONS_H - -#include - -BSON_BEGIN_DECLS - -BSON_EXPORT(int) -bson_get_major_version(void); -BSON_EXPORT(int) -bson_get_minor_version(void); -BSON_EXPORT(int) -bson_get_micro_version(void); -BSON_EXPORT(const char *) -bson_get_version(void); -BSON_EXPORT(bool) -bson_check_version(int required_major, int required_minor, int required_micro); - -BSON_END_DECLS - -#endif /* BSON_VERSION_FUNCTIONS_H */ diff --git a/bsonjs/bson/bson-writer.c b/bsonjs/bson/bson-writer.c deleted file mode 100644 index f2953c5..0000000 --- a/bsonjs/bson/bson-writer.c +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include - -#include - - -struct _bson_writer_t { - bool ready; - uint8_t **buf; - size_t *buflen; - size_t offset; - bson_realloc_func realloc_func; - void *realloc_func_ctx; - bson_t b; -}; - - -/* - *-------------------------------------------------------------------------- - * - * bson_writer_new -- - * - * Creates a new instance of bson_writer_t using the buffer, length, - * offset, and realloc() function supplied. - * - * The caller is expected to clean up the structure when finished - * using bson_writer_destroy(). - * - * Parameters: - * @buf: (inout): A pointer to a target buffer. - * @buflen: (inout): A pointer to the buffer length. - * @offset: The offset in the target buffer to start from. - * @realloc_func: A realloc() style function or NULL. - * - * Returns: - * A newly allocated bson_writer_t that should be freed with - * bson_writer_destroy(). - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bson_writer_t * -bson_writer_new(uint8_t **buf, /* IN */ - size_t *buflen, /* IN */ - size_t offset, /* IN */ - bson_realloc_func realloc_func, /* IN */ - void *realloc_func_ctx) /* IN */ -{ - bson_writer_t *writer; - - writer = BSON_ALIGNED_ALLOC0(bson_writer_t); - writer->buf = buf; - writer->buflen = buflen; - writer->offset = offset; - writer->realloc_func = realloc_func; - writer->realloc_func_ctx = realloc_func_ctx; - writer->ready = true; - - return writer; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_writer_destroy -- - * - * Cleanup after @writer and release any allocated memory. Note that - * the buffer supplied to bson_writer_new() is NOT freed from this - * method. The caller is responsible for that. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_writer_destroy(bson_writer_t *writer) /* IN */ -{ - bson_free(writer); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_writer_get_length -- - * - * Fetches the current length of the content written by the buffer - * (including the initial offset). This includes a partly written - * document currently being written. - * - * This is useful if you want to check to see if you've passed a given - * memory boundary that cannot be sent in a packet. See - * bson_writer_rollback() to abort the current document being written. - * - * Returns: - * The number of bytes written plus initial offset. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -size_t -bson_writer_get_length(bson_writer_t *writer) /* IN */ -{ - return writer->offset + writer->b.len; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_writer_begin -- - * - * Begins writing a new document. The caller may use the bson - * structure to write out a new BSON document. When completed, the - * caller must call either bson_writer_end() or - * bson_writer_rollback(). - * - * Parameters: - * @writer: A bson_writer_t. - * @bson: (out): A location for a bson_t*. - * - * Returns: - * true if the underlying realloc was successful; otherwise false. - * - * Side effects: - * @bson is initialized if true is returned. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_writer_begin(bson_writer_t *writer, /* IN */ - bson_t **bson) /* OUT */ -{ - bson_impl_alloc_t *b; - bool grown = false; - - BSON_ASSERT(writer); - BSON_ASSERT(writer->ready); - BSON_ASSERT(bson); - - writer->ready = false; - - memset(&writer->b, 0, sizeof(bson_t)); - - b = (bson_impl_alloc_t *)&writer->b; - b->flags = BSON_FLAG_NO_FREE_OBJECT | BSON_FLAG_NO_FREE_DATA; - b->len = 5; - b->parent = NULL; - b->indirect_buffer = writer->buf; - b->indirect_buflen = writer->buflen; - b->offset = writer->offset; - b->own_buffer = NULL; - b->own_buflen = 0; - b->realloc = writer->realloc_func; - b->realloc_func_ctx = writer->realloc_func_ctx; - - while ((writer->offset + writer->b.len) > *writer->buflen) { - if (!writer->realloc_func) { - memset(&writer->b, 0, sizeof(bson_t)); - writer->ready = true; - return false; - } - grown = true; - - if (!*writer->buflen) { - *writer->buflen = 64; - } else { - (*writer->buflen) *= 2; - } - } - - if (grown) { - *writer->buf = writer->realloc_func(*writer->buf, *writer->buflen, writer->realloc_func_ctx); - } - - memset((*writer->buf) + writer->offset + 1, 0, 5); - (*writer->buf)[writer->offset] = 5; - - *bson = &writer->b; - - return true; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_writer_end -- - * - * Complete writing of a bson_writer_t to the buffer supplied. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_writer_end(bson_writer_t *writer) /* IN */ -{ - BSON_ASSERT(writer); - BSON_ASSERT(!writer->ready); - - writer->offset += writer->b.len; - memset(&writer->b, 0, sizeof(bson_t)); - writer->ready = true; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_writer_rollback -- - * - * Abort the appending of the current bson_t to the memory region - * managed by @writer. This is useful if you detected that you went - * past a particular memory limit. For example, MongoDB has 48MB - * message limits. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_writer_rollback(bson_writer_t *writer) /* IN */ -{ - BSON_ASSERT(writer); - - if (writer->b.len) { - memset(&writer->b, 0, sizeof(bson_t)); - } - - writer->ready = true; -} diff --git a/bsonjs/bson/bson-writer.h b/bsonjs/bson/bson-writer.h deleted file mode 100644 index 64fecce..0000000 --- a/bsonjs/bson/bson-writer.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#ifndef BSON_WRITER_H -#define BSON_WRITER_H - -#include -#include - -#include - -BSON_BEGIN_DECLS - -/** - * bson_writer_t: - * - * The bson_writer_t structure is a helper for writing a series of BSON - * documents to a single malloc() buffer. You can provide a realloc() style - * function to grow the buffer as you go. - * - * This is useful if you want to build a series of BSON documents right into - * the target buffer for an outgoing packet. The offset parameter allows you to - * start at an offset of the target buffer. - */ -typedef struct _bson_writer_t bson_writer_t; - -BSON_EXPORT(bson_writer_t *) -bson_writer_new(uint8_t **buf, size_t *buflen, size_t offset, bson_realloc_func realloc_func, void *realloc_func_ctx); -BSON_EXPORT(void) -bson_writer_destroy(bson_writer_t *writer); -BSON_EXPORT(size_t) -bson_writer_get_length(bson_writer_t *writer); -BSON_EXPORT(bool) -bson_writer_begin(bson_writer_t *writer, bson_t **bson); -BSON_EXPORT(void) -bson_writer_end(bson_writer_t *writer); -BSON_EXPORT(void) -bson_writer_rollback(bson_writer_t *writer); - -BSON_END_DECLS - -#endif /* BSON_WRITER_H */ diff --git a/bsonjs/bson/bson.c b/bsonjs/bson/bson.c deleted file mode 100644 index b2fe9aa..0000000 --- a/bsonjs/bson/bson.c +++ /dev/null @@ -1,2869 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include - - -/* - * Globals. - */ -static const uint8_t gZero = 0; - -/* - *-------------------------------------------------------------------------- - * - * _bson_round_up_alloc_size -- - * - * Given a potential allocation length in bytes, round up to the - * next power of two without exceeding BSON_MAX_SIZE. - * - * Returns: - * If the input is <= BSON_MAX_SIZE, returns a value >= the input - * and still <= BSON_MAX_SIZE. If the input was greater than - * BSON_MAX_SIZE, it is returned unmodified. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static BSON_INLINE size_t -_bson_round_up_alloc_size(size_t size) -{ - if (size <= BSON_MAX_SIZE) { - size_t power_of_two = bson_next_power_of_two(size); - return BSON_MIN(power_of_two, BSON_MAX_SIZE); - } else { - return size; - } -} - -/* - *-------------------------------------------------------------------------- - * - * _bson_impl_inline_grow -- - * - * Document growth implementation for documents that currently - * contain stack based buffers. The document may be switched to - * a malloc based buffer. - * - * Returns: - * true if successful; otherwise false indicating BSON_MAX_SIZE overflow. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static bool -_bson_impl_inline_grow(bson_impl_inline_t *impl, /* IN */ - uint32_t grow_size) /* IN */ -{ - bson_impl_alloc_t *alloc = (bson_impl_alloc_t *)impl; - uint8_t *data; - - MONGOC_DEBUG_ASSERT((size_t)impl->len <= BSON_MAX_SIZE); - MONGOC_DEBUG_ASSERT((size_t)grow_size <= BSON_MAX_SIZE); - size_t req = (size_t)impl->len + (size_t)grow_size; - - if (req <= sizeof impl->data) { - return true; - } - - req = _bson_round_up_alloc_size(req); - - if (req <= BSON_MAX_SIZE) { - data = bson_malloc(req); - - memcpy(data, impl->data, impl->len); - alloc->flags &= ~BSON_FLAG_INLINE_DATA; - alloc->parent = NULL; - alloc->depth = 0; - alloc->indirect_buffer = NULL; - alloc->indirect_buflen = NULL; - alloc->offset = 0; - alloc->own_buffer = data; - alloc->own_buflen = req; - alloc->realloc = bson_realloc_ctx; - alloc->realloc_func_ctx = NULL; - - return true; - } - - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_impl_alloc_grow -- - * - * Document growth implementation for non-inline documents, possibly - * containing a reallocatable buffer. - * - * Returns: - * true if successful; otherwise false indicating BSON_MAX_SIZE overflow - * or an attempt to grow a buffer with no realloc implementation. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static bool -_bson_impl_alloc_grow(bson_impl_alloc_t *impl, /* IN */ - uint32_t grow_size) /* IN */ -{ - /* Determine how many bytes we need for this document in the buffer - * including necessary trailing bytes for parent documents. - * - * On size assumptions: the previous grow operation has already checked - * (len + offset + previous_depth) against BSON_MAX_SIZE. Current depth can be at most (previous_depth + 1). The - * caller has checked grow_size against BSON_MAX_SIZE. On the smallest (32-bit) supported size_t, we can still add - * these maximum values (2x BSON_MAX_SIZE, 1 additional byte of depth) without arithmetic overflow. - */ - MONGOC_DEBUG_ASSERT((uint64_t)impl->len + (uint64_t)impl->offset + (uint64_t)impl->depth <= (uint64_t)BSON_MAX_SIZE); - MONGOC_DEBUG_ASSERT((size_t)grow_size <= BSON_MAX_SIZE); - size_t req = impl->offset + (size_t)impl->len + (size_t)grow_size + (size_t)impl->depth; - - uint8_t **const buf_to_grow = impl->indirect_buffer ? impl->indirect_buffer : &impl->own_buffer; - size_t *const sz_to_grow = impl->indirect_buffer ? impl->indirect_buflen : &impl->own_buflen; - - if (req <= *sz_to_grow) { - return true; - } - - req = _bson_round_up_alloc_size(req); - - if ((req <= BSON_MAX_SIZE) && impl->realloc) { - *buf_to_grow = impl->realloc(*buf_to_grow, req, impl->realloc_func_ctx); - *sz_to_grow = req; - return true; - } - - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_grow -- - * - * Grows the bson_t structure to be large enough to contain @grow_size - * bytes in addition to its current content. - * - * The caller is responsible for ensuring @grow_size itself is not - * above BSON_MAX_SIZE, but a final determination of overflow status - * can't be made until we are inside _bson_impl_*_grow(). - * - * Returns: - * true if successful, false if the size would overflow or the buffer - * needs to grow but does not support reallocation. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static bool -_bson_grow(bson_t *bson, /* IN */ - uint32_t grow_size) /* IN */ -{ - BSON_ASSERT((size_t)grow_size <= BSON_MAX_SIZE); - - if ((bson->flags & BSON_FLAG_INLINE_DATA)) { - return _bson_impl_inline_grow((bson_impl_inline_t *)bson, grow_size); - } - - return _bson_impl_alloc_grow((bson_impl_alloc_t *)bson, grow_size); -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_data -- - * - * A helper function to return the contents of the bson document - * taking into account the polymorphic nature of bson_t. - * - * Returns: - * A buffer which should not be modified or freed. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static BSON_INLINE uint8_t * -_bson_data(const bson_t *bson) /* IN */ -{ - if ((bson->flags & BSON_FLAG_INLINE_DATA)) { - return ((bson_impl_inline_t *)bson)->data; - } else { - bson_impl_alloc_t *impl = (bson_impl_alloc_t *)bson; - uint8_t *base = impl->indirect_buffer ? *impl->indirect_buffer : impl->own_buffer; - return base + impl->offset; - } -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_encode_length -- - * - * Helper to encode the length of the bson_t in the first 4 bytes - * of the bson document. Little endian format is used as specified - * by bsonspec. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static BSON_INLINE void -_bson_encode_length(bson_t *bson) /* IN */ -{ - mlib_write_u32le(_bson_data(bson), bson->len); -} - - -typedef struct _bson_append_bytes_arg { - const uint8_t *bytes; // Optional. - uint32_t length; // > 0. -} _bson_append_bytes_arg; - -typedef struct _bson_append_bytes_list { - _bson_append_bytes_arg args[8]; // Arbitrary length: just needs to be large enough. - _bson_append_bytes_arg *current; // "Insert"/"End" pointer. - uint32_t n_bytes; // Total bytes to be appended. -} _bson_append_bytes_list; - -// To support unchecked cast from non-negative `int` to `size_t`. -BSON_STATIC_ASSERT2(size_t_gte_int, SIZE_MAX >= INT_MAX); - -// To support unchecked cast from `uint32_t` to `size_t`. -BSON_STATIC_ASSERT2(size_t_gte_uint32_t, SIZE_MAX >= UINT32_MAX); - -// Support largest _bson_impl_alloc_grow on smallest size_t -BSON_STATIC_ASSERT2(max_alloc_grow_fits_min_sizet, (uint64_t)BSON_MAX_SIZE * 2u + 1u <= (uint64_t)UINT32_MAX); - -// Declare local state with the identifier `ident`. -#define BSON_APPEND_BYTES_LIST_DECLARE(ident) \ - _bson_append_bytes_list ident = {.current = (ident).args, .n_bytes = 0u}; \ - ((void)0) - -// Add a bytes+length pair only if `_length > 0`. -// Append failure if `n_bytes` will exceed BSON max size. -#define BSON_APPEND_BYTES_ADD_ARGUMENT(_list, _bytes, _length) \ - mlib_diagnostic_push(); \ - mlib_disable_constant_conditional_expression_warnings(); \ - if (BSON_UNLIKELY((_length) > BSON_MAX_SIZE - (_list).n_bytes)) { \ - mlib_diagnostic_pop(); \ - goto append_failure; \ - } else if ((_length) > 0) { \ - *(_list).current++ = (_bson_append_bytes_arg){ \ - .bytes = (const uint8_t *)(_bytes), \ - .length = (_length), \ - }; \ - (_list).n_bytes += (_length); \ - } else \ - ((void)0) - -// Add a UTF-8 string only if no embedded null bytes are present. -// Uses `strlen (_key)` when `_key_len < 0`, otherwise uses `_key_len`. -#define BSON_APPEND_BYTES_ADD_CHECKED_STRING(_list, _key, _key_len) \ - uint32_t BSON_CONCAT(key_ulen_, __LINE__); \ - if ((_key_len) < 0) { \ - const size_t key_zulen = strlen((_key)); \ - if (BSON_UNLIKELY(key_zulen > UINT32_MAX)) { \ - goto append_failure; \ - } \ - BSON_CONCAT(key_ulen_, __LINE__) = (uint32_t)key_zulen; \ - } else { \ - const size_t key_zulen = (size_t)(_key_len); \ - if (BSON_UNLIKELY(key_zulen > UINT32_MAX)) { \ - goto append_failure; \ - } /* Necessary to validate embedded NULL is not present in key. */ \ - else if (memchr((_key), '\0', key_zulen) != NULL) { \ - goto append_failure; \ - } else { \ - BSON_CONCAT(key_ulen_, __LINE__) = (uint32_t)key_zulen; \ - } \ - } \ - BSON_APPEND_BYTES_ADD_ARGUMENT((_list), (_key), BSON_CONCAT(key_ulen_, __LINE__)) - -// Apply the list of arguments to be appended to `_bson`. -// Append failure if adding `_list.n_bytes` will exceed BSON max size. -#define BSON_APPEND_BYTES_APPLY_ARGUMENTS(_bson, _list) \ - if (BSON_UNLIKELY((_list).n_bytes > BSON_MAX_SIZE - (_bson)->len)) { \ - goto append_failure; \ - } else if (BSON_UNLIKELY(!_bson_grow((_bson), (_list).n_bytes))) { \ - goto append_failure; \ - } else { \ - uint8_t *data = _bson_data((_bson)) + ((_bson)->len - 1u); \ - for (const _bson_append_bytes_arg *arg = (_list).args; arg != (_list).current; ++arg) { \ - if (arg->bytes) { \ - memcpy(data, arg->bytes, arg->length); \ - } \ - (_bson)->len += arg->length; \ - data += arg->length; \ - } \ - _bson_encode_length((_bson)); \ - data[0] = '\0'; \ - } \ - ((void)0) - - -/* - *-------------------------------------------------------------------------- - * - * _bson_append_bson_begin -- - * - * Begin appending a subdocument or subarray to the document using - * the key provided by @key. - * - * If @key_length is < 0, then strlen() will be called on @key - * to determine the length. - * - * @key_type MUST be either BSON_TYPE_DOCUMENT or BSON_TYPE_ARRAY. - * - * Returns: - * true if successful; otherwise false indicating BSON_MAX_SIZE overflow. - * - * Side effects: - * @child is initialized if true is returned. - * - *-------------------------------------------------------------------------- - */ - -static bool -_bson_append_bson_begin(bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - bson_type_t child_type, /* IN */ - bson_t *child) /* OUT */ -{ - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - BSON_ASSERT_PARAM(child); - - BSON_ASSERT(!(bson->flags & BSON_FLAG_RDONLY)); - BSON_ASSERT(!(bson->flags & BSON_FLAG_IN_CHILD)); - BSON_ASSERT((child_type == BSON_TYPE_DOCUMENT) || (child_type == BSON_TYPE_ARRAY)); - - { - BSON_APPEND_BYTES_LIST_DECLARE(args); - - const uint8_t type = (uint8_t)child_type; - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - - /* - * If the parent is an inline bson_t, then we need to convert - * it to a heap allocated buffer. This makes extending buffers - * of child bson documents much simpler logic, as they can just - * realloc the *buf pointer. - */ - if ((bson->flags & BSON_FLAG_INLINE_DATA)) { - BSON_ASSERT(bson->len <= 120); - if (!_bson_grow(bson, 128 - bson->len)) { - return false; - } - BSON_ASSERT(!(bson->flags & BSON_FLAG_INLINE_DATA)); - } - - const uint8_t empty[5] = {5}; - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &empty, sizeof(empty)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - goto append_success; - - append_failure: - return false; - } - -append_success: - ((void)0); - - bson_impl_alloc_t *aparent = (bson_impl_alloc_t *)bson; - bson_impl_alloc_t *achild = (bson_impl_alloc_t *)child; - - /* - * Mark the document as working on a child document so that no - * further modifications can happen until the caller has called - * bson_append_{document,array}_end(). - */ - bson->flags |= BSON_FLAG_IN_CHILD; - - /* - * Initialize the child bson_t structure and point it at the parents - * buffers. This allows us to realloc directly from the child without - * walking up to the parent bson_t. - */ - achild->flags = (BSON_FLAG_CHILD | BSON_FLAG_NO_FREE_DATA | BSON_FLAG_NO_FREE_OBJECT); - - if ((bson->flags & BSON_FLAG_CHILD)) { - achild->depth = ((bson_impl_alloc_t *)bson)->depth + 1; - } else { - achild->depth = 1; - } - - achild->parent = bson; - achild->indirect_buffer = aparent->indirect_buffer ? aparent->indirect_buffer : &aparent->own_buffer; - achild->indirect_buflen = aparent->indirect_buflen ? aparent->indirect_buflen : &aparent->own_buflen; - achild->offset = aparent->offset + aparent->len - 1 - 5; - achild->len = 5; - achild->own_buffer = NULL; - achild->own_buflen = 0; - achild->realloc = aparent->realloc; - achild->realloc_func_ctx = aparent->realloc_func_ctx; - - return true; -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_append_bson_end -- - * - * Complete a call to _bson_append_bson_begin. - * - * Returns: - * true if successful. - * - * Side effects: - * @child is destroyed and no longer valid after calling this - * function. - * - *-------------------------------------------------------------------------- - */ - -static bool -_bson_append_bson_end(bson_t *bson, /* IN */ - bson_t *child) /* IN */ -{ - BSON_ASSERT(bson); - BSON_ASSERT((bson->flags & BSON_FLAG_IN_CHILD)); - BSON_ASSERT(!(child->flags & BSON_FLAG_IN_CHILD)); - - /* - * Unmark the IN_CHILD flag. - */ - bson->flags &= ~BSON_FLAG_IN_CHILD; - - /* - * Now that we are done building the sub-document, add the size to the - * parent, not including the default 5 byte empty document already added. - */ - bson->len = (bson->len + child->len - 5); - - /* - * Ensure we have a \0 byte at the end and proper length encoded at - * the beginning of the document. - */ - _bson_data(bson)[bson->len - 1] = '\0'; - _bson_encode_length(bson); - - return true; -} - -bool -bson_append_array_begin(bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - bson_t *child) /* IN */ -{ - return bson_append_array_unsafe_begin(bson, key, key_length, child); -} - -bool -bson_append_array_unsafe_begin(bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - bson_t *child) /* IN */ -{ - BSON_ASSERT(bson); - BSON_ASSERT(key); - BSON_ASSERT(child); - - return _bson_append_bson_begin(bson, key, key_length, BSON_TYPE_ARRAY, child); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_append_array_end -- - * - * Complete a call to bson_append_array_begin(). - * - * It is safe to append other fields to @bson after calling this - * function. - * - * Returns: - * true if successful. - * - * Side effects: - * @child is invalid after calling this function. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_append_array_end(bson_t *bson, /* IN */ - bson_t *child) /* IN */ -{ - BSON_ASSERT(bson); - BSON_ASSERT(child); - - return _bson_append_bson_end(bson, child); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_append_document_begin -- - * - * Start appending a new document. - * - * Use @child to append to the data area for the given field. - * - * It is a programming error to call any other bson function on - * @bson until bson_append_document_end() has been called. It is - * valid to call bson_append*() functions on @child. - * - * This function is useful to allow building nested documents using - * a single buffer owned by the top-level bson document. - * - * Returns: - * true if successful; otherwise false and @child is invalid. - * - * Side effects: - * @child is initialized if true is returned. - * - *-------------------------------------------------------------------------- - */ -bool -bson_append_document_begin(bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - bson_t *child) /* IN */ -{ - BSON_ASSERT(bson); - BSON_ASSERT(key); - BSON_ASSERT(child); - - return _bson_append_bson_begin(bson, key, key_length, BSON_TYPE_DOCUMENT, child); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_append_document_end -- - * - * Complete a call to bson_append_document_begin(). - * - * It is safe to append new fields to @bson after calling this - * function, if true is returned. - * - * Returns: - * true if successful; otherwise false indicating BSON_MAX_SIZE overflow. - * - * Side effects: - * @child is destroyed and invalid after calling this function. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_append_document_end(bson_t *bson, /* IN */ - bson_t *child) /* IN */ -{ - BSON_ASSERT(bson); - BSON_ASSERT(child); - - return _bson_append_bson_end(bson, child); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_append_array -- - * - * Append an array to @bson. - * - * Generally, bson_append_array_begin() will result in faster code - * since few buffers need to be malloced. - * - * Returns: - * true if successful; otherwise false indicating BSON_MAX_SIZE overflow. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_append_array(bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - const bson_t *array) /* IN */ -{ - static const uint8_t type = BSON_TYPE_ARRAY; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - BSON_ASSERT_PARAM(array); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - - /* - * Let's be a bit pedantic and ensure the array has properly formatted key - * names. We will verify this simply by checking the first element for "0" - * if the array is non-empty. - */ - if (array && !bson_empty(array)) { - bson_iter_t iter; - - if (bson_iter_init(&iter, array) && bson_iter_next(&iter)) { - if (0 != strcmp("0", bson_iter_key(&iter))) { - fprintf(stderr, - "%s(): invalid array detected. first element of array " - "parameter is not \"0\".\n", - BSON_FUNC); - } - } - } - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, _bson_data(array), array->len); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * _bson_append_binary -- - * - * Append a BSON_TYPE_BINARY field, optionally copying @binary into the field. - * - * Parameters: - * @subtype: the BSON Binary Subtype. See bsonspec.org for more - * information. - * @binary: Optional pointer to the raw binary data. - * @length: the size of the field's binary data in bytes. - * - * Returns: - * true if successful; otherwise false. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -static bool -_bson_append_binary(bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - bson_subtype_t subtype, /* IN */ - const uint8_t *binary, /* IN */ - uint32_t length) /* IN */ -{ - static const uint8_t type = BSON_TYPE_BINARY; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - BSON_OPTIONAL_PARAM(binary); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - - const uint8_t subtype_arg = (uint8_t)subtype; - - if (subtype == BSON_SUBTYPE_BINARY_DEPRECATED) { - if (length > UINT32_MAX - 4u) { - return false; - } - - const uint32_t length_le = BSON_UINT32_TO_LE(length + 4u); - const uint32_t length_arg = BSON_UINT32_TO_LE(length); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &length_le, sizeof(length_le)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &subtype_arg, sizeof(subtype_arg)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &length_arg, sizeof(length_arg)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, binary, length); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - } else { - const uint32_t length_arg = BSON_UINT32_TO_LE(length); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &length_arg, sizeof(length_arg)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &subtype_arg, sizeof(subtype_arg)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, binary, length); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - } - - return true; - -append_failure: - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_append_binary -- - * - * Append binary data to @bson. The field will have the - * BSON_TYPE_BINARY type. - * - * Parameters: - * @subtype: the BSON Binary Subtype. See bsonspec.org for more - * information. - * @binary: a pointer to the raw binary data. - * @length: the size of @binary in bytes. - * - * Returns: - * true if successful; otherwise false. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_append_binary(bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - bson_subtype_t subtype, /* IN */ - const uint8_t *binary, /* IN */ - uint32_t length) /* IN */ -{ - if (!binary && length > 0u) { - return false; - } - return _bson_append_binary(bson, key, key_length, subtype, binary, length); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_append_binary_uninit -- - * - * Append binary data to @bson by framing an uninitialized field to be written by the caller. - * The field will have the BSON_TYPE_BINARY type. On success, the caller MUST write to all - * bytes in the binary data field. The returned `*binary` pointer may be invalidated by - * subsequent modifications to @bson. - * - * Parameters: - * @subtype: the BSON Binary Subtype. See bsonspec.org for more - * information. - * @binary: Output parameter for a temporary pointer where the binary item's contents must be written. - * @length: the size of @binary in bytes. - * - * Returns: - * true if successful; otherwise false. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_append_binary_uninit(bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - bson_subtype_t subtype, /* IN */ - uint8_t **binary, /* IN */ - uint32_t length) /* IN */ -{ - BSON_ASSERT_PARAM(binary); - if (_bson_append_binary(bson, key, key_length, subtype, NULL, length)) { - *binary = _bson_data(bson) + bson->len - 1u - length; - return true; - } else { - return false; - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_append_bool -- - * - * Append a new field to @bson with the name @key. The value is - * a boolean indicated by @value. - * - * Returns: - * true if successful; otherwise false. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_append_bool(bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - bool value) /* IN */ -{ - static const uint8_t type = BSON_TYPE_BOOL; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - - const uint8_t byte_arg = value ? 1u : 0u; - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &byte_arg, sizeof(byte_arg)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_append_code -- - * - * Append a new field to @bson containing javascript code. - * - * @javascript MUST be a zero terminated UTF-8 string. It MUST NOT - * containing embedded \0 characters. - * - * Returns: - * true if successful; otherwise false. - * - * Side effects: - * None. - * - * See also: - * bson_append_code_with_scope(). - * - *-------------------------------------------------------------------------- - */ - -bool -bson_append_code(bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - const char *javascript) /* IN */ -{ - static const uint8_t type = BSON_TYPE_CODE; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - BSON_ASSERT_PARAM(javascript); - - const size_t zulength = strlen(javascript); - if (zulength > UINT32_MAX - 1u) { - return false; - } - const uint32_t length = (uint32_t)zulength + 1u; - const uint32_t length_arg = BSON_UINT32_TO_LE(length); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &length_arg, sizeof(length_arg)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, javascript, length); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_append_code_with_scope -- - * - * Append a new field to @bson containing javascript code with - * supplied scope. - * - * Returns: - * true if successful; otherwise false. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_append_code_with_scope(bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - const char *javascript, /* IN */ - const bson_t *scope) /* IN */ -{ - static const uint8_t type = BSON_TYPE_CODEWSCOPE; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - BSON_ASSERT_PARAM(javascript); - - if (!scope) { - return bson_append_code(bson, key, key_length, javascript); - } - - const size_t zulength = strlen(javascript); - if (zulength > UINT32_MAX - 1u) { - return false; - } - - const uint32_t js_length = (uint32_t)zulength + 1u; - const uint32_t js_length_arg = BSON_UINT32_TO_LE(js_length); - - if (js_length > UINT32_MAX - scope->len) { - return false; - } - - if (js_length + scope->len > UINT32_MAX - (2u * sizeof(uint32_t))) { - return false; - } - - const uint32_t total_length = (uint32_t)(2u * sizeof(uint32_t)) + js_length + scope->len; - const uint32_t total_length_arg = BSON_UINT32_TO_LE(total_length); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &total_length_arg, sizeof(total_length_arg)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &js_length_arg, sizeof(js_length_arg)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, javascript, js_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, _bson_data(scope), scope->len); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_append_dbpointer -- - * - * This BSON data type is DEPRECATED. - * - * Append a BSON dbpointer field to @bson. - * - * Returns: - * true if successful; otherwise false. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -bool -bson_append_dbpointer(bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - const char *collection, /* IN */ - const bson_oid_t *oid) -{ - static const uint8_t type = BSON_TYPE_DBPOINTER; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - BSON_ASSERT_PARAM(collection); - BSON_ASSERT_PARAM(oid); - - const size_t zulength = strlen(collection); - if (zulength > UINT32_MAX - 1u) { - return false; - } - - const uint32_t length = (uint32_t)zulength + 1u; - const uint32_t length_arg = BSON_UINT32_TO_LE(length); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &length_arg, sizeof(length_arg)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, collection, length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, oid->bytes, sizeof(oid->bytes)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_append_document -- - * - * Append a new field to @bson containing a BSON document. - * - * In general, using bson_append_document_begin() results in faster - * code and less memory fragmentation. - * - * Returns: - * true if successful; otherwise false. - * - * Side effects: - * None. - * - * See also: - * bson_append_document_begin(). - * - *-------------------------------------------------------------------------- - */ - -bool -bson_append_document(bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - const bson_t *value) /* IN */ -{ - static const uint8_t type = BSON_TYPE_DOCUMENT; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - BSON_ASSERT_PARAM(value); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, _bson_data(value), value->len); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_double(bson_t *bson, const char *key, int key_length, double value) -{ - static const uint8_t type = BSON_TYPE_DOUBLE; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - const double value_arg = BSON_DOUBLE_TO_LE(value); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &value_arg, sizeof(value_arg)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_int32(bson_t *bson, const char *key, int key_length, int32_t value) -{ - static const uint8_t type = BSON_TYPE_INT32; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - const uint32_t value_arg = BSON_UINT32_TO_LE(value); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &value_arg, sizeof(value_arg)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_int64(bson_t *bson, const char *key, int key_length, int64_t value) -{ - static const uint8_t type = BSON_TYPE_INT64; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - const uint64_t value_arg = BSON_UINT64_TO_LE(value); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &value_arg, sizeof(value_arg)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_decimal128(bson_t *bson, const char *key, int key_length, const bson_decimal128_t *value) -{ - static const uint8_t type = BSON_TYPE_DECIMAL128; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - const uint64_t value_arg[] = { - BSON_UINT64_TO_LE(value->low), - BSON_UINT64_TO_LE(value->high), - }; - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &value_arg, sizeof(value_arg)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_iter(bson_t *bson, const char *key, int key_length, const bson_iter_t *iter) -{ - bool ret = false; - - BSON_ASSERT(bson); - BSON_ASSERT(iter); - - if (!key) { - key = bson_iter_key(iter); - key_length = -1; - } - - switch (bson_iter_type_unsafe(iter)) { - case BSON_TYPE_EOD: - return false; - case BSON_TYPE_DOUBLE: - ret = bson_append_double(bson, key, key_length, bson_iter_double(iter)); - break; - case BSON_TYPE_UTF8: { - uint32_t len = 0; - const char *str; - - str = bson_iter_utf8(iter, &len); - ret = bson_append_utf8(bson, key, key_length, str, len); - } break; - case BSON_TYPE_DOCUMENT: { - const uint8_t *buf = NULL; - uint32_t len = 0; - bson_t doc; - - bson_iter_document(iter, &len, &buf); - - if (bson_init_static(&doc, buf, len)) { - ret = bson_append_document(bson, key, key_length, &doc); - bson_destroy(&doc); - } - } break; - case BSON_TYPE_ARRAY: { - const uint8_t *buf = NULL; - uint32_t len = 0; - bson_t doc; - - bson_iter_array(iter, &len, &buf); - - if (bson_init_static(&doc, buf, len)) { - ret = bson_append_array(bson, key, key_length, &doc); - bson_destroy(&doc); - } - } break; - case BSON_TYPE_BINARY: { - const uint8_t *binary = NULL; - bson_subtype_t subtype = BSON_SUBTYPE_BINARY; - uint32_t len = 0; - - bson_iter_binary(iter, &subtype, &len, &binary); - ret = bson_append_binary(bson, key, key_length, subtype, binary, len); - } break; - case BSON_TYPE_UNDEFINED: - ret = bson_append_undefined(bson, key, key_length); - break; - case BSON_TYPE_OID: - ret = bson_append_oid(bson, key, key_length, bson_iter_oid(iter)); - break; - case BSON_TYPE_BOOL: - ret = bson_append_bool(bson, key, key_length, bson_iter_bool(iter)); - break; - case BSON_TYPE_DATE_TIME: - ret = bson_append_date_time(bson, key, key_length, bson_iter_date_time(iter)); - break; - case BSON_TYPE_NULL: - ret = bson_append_null(bson, key, key_length); - break; - case BSON_TYPE_REGEX: { - const char *regex; - const char *options; - - regex = bson_iter_regex(iter, &options); - ret = bson_append_regex(bson, key, key_length, regex, options); - } break; - case BSON_TYPE_DBPOINTER: { - const bson_oid_t *oid; - uint32_t len; - const char *collection; - - bson_iter_dbpointer(iter, &len, &collection, &oid); - ret = bson_append_dbpointer(bson, key, key_length, collection, oid); - } break; - case BSON_TYPE_CODE: { - uint32_t len; - const char *code; - - code = bson_iter_code(iter, &len); - ret = bson_append_code(bson, key, key_length, code); - } break; - case BSON_TYPE_SYMBOL: { - uint32_t len; - const char *symbol; - - symbol = bson_iter_symbol(iter, &len); - ret = bson_append_symbol(bson, key, key_length, symbol, len); - } break; - case BSON_TYPE_CODEWSCOPE: { - const uint8_t *scope = NULL; - uint32_t scope_len = 0; - uint32_t len = 0; - const char *javascript = NULL; - bson_t doc; - - javascript = bson_iter_codewscope(iter, &len, &scope_len, &scope); - - if (bson_init_static(&doc, scope, scope_len)) { - ret = bson_append_code_with_scope(bson, key, key_length, javascript, &doc); - bson_destroy(&doc); - } - } break; - case BSON_TYPE_INT32: - ret = bson_append_int32(bson, key, key_length, bson_iter_int32(iter)); - break; - case BSON_TYPE_TIMESTAMP: { - uint32_t ts; - uint32_t inc; - - bson_iter_timestamp(iter, &ts, &inc); - ret = bson_append_timestamp(bson, key, key_length, ts, inc); - } break; - case BSON_TYPE_INT64: - ret = bson_append_int64(bson, key, key_length, bson_iter_int64(iter)); - break; - case BSON_TYPE_DECIMAL128: { - bson_decimal128_t dec; - - if (!bson_iter_decimal128(iter, &dec)) { - return false; - } - - ret = bson_append_decimal128(bson, key, key_length, &dec); - } break; - case BSON_TYPE_MAXKEY: - ret = bson_append_maxkey(bson, key, key_length); - break; - case BSON_TYPE_MINKEY: - ret = bson_append_minkey(bson, key, key_length); - break; - default: - break; - } - - return ret; -} - - -bool -bson_append_maxkey(bson_t *bson, const char *key, int key_length) -{ - static const uint8_t type = BSON_TYPE_MAXKEY; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_minkey(bson_t *bson, const char *key, int key_length) -{ - static const uint8_t type = BSON_TYPE_MINKEY; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_null(bson_t *bson, const char *key, int key_length) -{ - static const uint8_t type = BSON_TYPE_NULL; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_oid(bson_t *bson, const char *key, int key_length, const bson_oid_t *value) -{ - static const uint8_t type = BSON_TYPE_OID; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - BSON_ASSERT_PARAM(value); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, value->bytes, sizeof(value->bytes)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_regex(bson_t *bson, const char *key, int key_length, const char *regex, const char *options) -{ - return bson_append_regex_w_len(bson, key, key_length, regex, -1, options); -} - - -bool -bson_append_regex_w_len( - bson_t *bson, const char *key, int key_length, const char *regex, int regex_length, const char *options) -{ - static const uint8_t type = BSON_TYPE_REGEX; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - bool ret = false; - - if (!regex) { - regex = ""; - } - - if (!options) { - options = ""; - } - - size_t options_len = strlen(options); - mcommon_string_append_t options_sorted; - mcommon_string_new_with_capacity_as_append(&options_sorted, (uint32_t)options_len); - if (!mcommon_string_append_selected_chars(&options_sorted, BSON_REGEX_OPTIONS_SORTED, options, options_len)) { - goto append_failure; - } - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, regex, regex_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT( - args, mcommon_str_from_append(&options_sorted), 1u + mcommon_strlen_from_append(&options_sorted)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - ret = true; - -append_failure: - mcommon_string_from_append_destroy(&options_sorted); - - return ret; -} - - -bool -bson_append_utf8(bson_t *bson, const char *key, int key_length, const char *value, int length) -{ - static const uint8_t type = BSON_TYPE_UTF8; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - if (BSON_UNLIKELY(!value)) { - return bson_append_null(bson, key, key_length); - } - - size_t zulength; - if (BSON_UNLIKELY(length < 0)) { - zulength = strlen(value); - } else { - zulength = (size_t)length; - } - - if (zulength > UINT32_MAX - 1u) { - return false; - } - - const uint32_t ulength = (uint32_t)zulength; - const uint32_t ulength_arg = BSON_UINT32_TO_LE(ulength + 1u); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &ulength_arg, sizeof(ulength_arg)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, value, ulength); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_symbol(bson_t *bson, const char *key, int key_length, const char *value, int length) -{ - static const uint8_t type = BSON_TYPE_SYMBOL; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - if (!value) { - return bson_append_null(bson, key, key_length); - } - - size_t zulength; - if (BSON_UNLIKELY(length < 0)) { - zulength = strlen(value); - } else { - zulength = (size_t)length; - } - - if (zulength > UINT32_MAX - 1u) { - return false; - } - - const uint32_t ulength = (uint32_t)zulength; - const uint32_t ulength_arg = BSON_UINT32_TO_LE(ulength + 1u); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &ulength_arg, sizeof(ulength_arg)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, value, ulength); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_time_t(bson_t *bson, const char *key, int key_length, time_t value) -{ -#ifdef BSON_OS_WIN32 - struct timeval tv = {(long)value, 0}; -#else - struct timeval tv = {value, 0}; -#endif - - BSON_ASSERT(bson); - BSON_ASSERT(key); - - return bson_append_timeval(bson, key, key_length, &tv); -} - - -bool -bson_append_timestamp(bson_t *bson, const char *key, int key_length, uint32_t timestamp, uint32_t increment) -{ - static const uint8_t type = BSON_TYPE_TIMESTAMP; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - const uint64_t value = BSON_UINT64_TO_LE(((((uint64_t)timestamp) << 32) | ((uint64_t)increment))); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &value, sizeof(value)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_now_utc(bson_t *bson, const char *key, int key_length) -{ - BSON_ASSERT(bson); - BSON_ASSERT(key); - BSON_ASSERT(key_length >= -1); - - return bson_append_time_t(bson, key, key_length, time(NULL)); -} - - -bool -bson_append_date_time(bson_t *bson, const char *key, int key_length, int64_t value) -{ - static const uint8_t type = BSON_TYPE_DATE_TIME; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - const uint64_t value_arg = BSON_UINT64_TO_LE(value); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &value_arg, sizeof(value_arg)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_timeval(bson_t *bson, const char *key, int key_length, struct timeval *value) -{ - uint64_t unix_msec; - - BSON_ASSERT(bson); - BSON_ASSERT(key); - BSON_ASSERT(value); - - unix_msec = (((uint64_t)value->tv_sec) * 1000UL) + (value->tv_usec / 1000UL); - return bson_append_date_time(bson, key, key_length, unix_msec); -} - - -bool -bson_append_undefined(bson_t *bson, const char *key, int key_length) -{ - static const uint8_t type = BSON_TYPE_UNDEFINED; - - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &type, sizeof(type)); - BSON_APPEND_BYTES_ADD_CHECKED_STRING(args, key, key_length); - BSON_APPEND_BYTES_ADD_ARGUMENT(args, &gZero, sizeof(gZero)); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(bson, args); - - return true; - -append_failure: - return false; -} - - -bool -bson_append_value(bson_t *bson, const char *key, int key_length, const bson_value_t *value) -{ - bson_t local; - bool ret = false; - - BSON_ASSERT(bson); - BSON_ASSERT(key); - BSON_ASSERT(value); - - switch (value->value_type) { - case BSON_TYPE_DOUBLE: - ret = bson_append_double(bson, key, key_length, value->value.v_double); - break; - case BSON_TYPE_UTF8: - ret = bson_append_utf8(bson, key, key_length, value->value.v_utf8.str, value->value.v_utf8.len); - break; - case BSON_TYPE_DOCUMENT: - if (bson_init_static(&local, value->value.v_doc.data, value->value.v_doc.data_len)) { - ret = bson_append_document(bson, key, key_length, &local); - bson_destroy(&local); - } - break; - case BSON_TYPE_ARRAY: - if (bson_init_static(&local, value->value.v_doc.data, value->value.v_doc.data_len)) { - ret = bson_append_array(bson, key, key_length, &local); - bson_destroy(&local); - } - break; - case BSON_TYPE_BINARY: - ret = bson_append_binary(bson, - key, - key_length, - value->value.v_binary.subtype, - value->value.v_binary.data, - value->value.v_binary.data_len); - break; - case BSON_TYPE_UNDEFINED: - ret = bson_append_undefined(bson, key, key_length); - break; - case BSON_TYPE_OID: - ret = bson_append_oid(bson, key, key_length, &value->value.v_oid); - break; - case BSON_TYPE_BOOL: - ret = bson_append_bool(bson, key, key_length, value->value.v_bool); - break; - case BSON_TYPE_DATE_TIME: - ret = bson_append_date_time(bson, key, key_length, value->value.v_datetime); - break; - case BSON_TYPE_NULL: - ret = bson_append_null(bson, key, key_length); - break; - case BSON_TYPE_REGEX: - ret = bson_append_regex(bson, key, key_length, value->value.v_regex.regex, value->value.v_regex.options); - break; - case BSON_TYPE_DBPOINTER: - ret = bson_append_dbpointer( - bson, key, key_length, value->value.v_dbpointer.collection, &value->value.v_dbpointer.oid); - break; - case BSON_TYPE_CODE: - ret = bson_append_code(bson, key, key_length, value->value.v_code.code); - break; - case BSON_TYPE_SYMBOL: - ret = bson_append_symbol(bson, key, key_length, value->value.v_symbol.symbol, value->value.v_symbol.len); - break; - case BSON_TYPE_CODEWSCOPE: - if (bson_init_static(&local, value->value.v_codewscope.scope_data, value->value.v_codewscope.scope_len)) { - ret = bson_append_code_with_scope(bson, key, key_length, value->value.v_codewscope.code, &local); - bson_destroy(&local); - } - break; - case BSON_TYPE_INT32: - ret = bson_append_int32(bson, key, key_length, value->value.v_int32); - break; - case BSON_TYPE_TIMESTAMP: - ret = bson_append_timestamp( - bson, key, key_length, value->value.v_timestamp.timestamp, value->value.v_timestamp.increment); - break; - case BSON_TYPE_INT64: - ret = bson_append_int64(bson, key, key_length, value->value.v_int64); - break; - case BSON_TYPE_DECIMAL128: - ret = bson_append_decimal128(bson, key, key_length, &(value->value.v_decimal128)); - break; - case BSON_TYPE_MAXKEY: - ret = bson_append_maxkey(bson, key, key_length); - break; - case BSON_TYPE_MINKEY: - ret = bson_append_minkey(bson, key, key_length); - break; - case BSON_TYPE_EOD: - default: - break; - } - - return ret; -} - - -void -bson_init(bson_t *bson) -{ - bson_impl_inline_t *impl = (bson_impl_inline_t *)bson; - - BSON_ASSERT(bson); - - impl->flags = BSON_FLAG_INLINE_DATA | BSON_FLAG_NO_FREE_OBJECT; - impl->len = 5; - impl->data[0] = 5; - impl->data[1] = 0; - impl->data[2] = 0; - impl->data[3] = 0; - impl->data[4] = 0; -} - - -void -bson_reinit(bson_t *bson) -{ - uint8_t *data; - - BSON_ASSERT(bson); - - data = _bson_data(bson); - - bson->len = 5; - - data[0] = 5; - data[1] = 0; - data[2] = 0; - data[3] = 0; - data[4] = 0; -} - - -bool -bson_init_static(bson_t *bson, const uint8_t *data, size_t length) -{ - bson_impl_alloc_t *impl = (bson_impl_alloc_t *)bson; - - BSON_ASSERT(bson); - BSON_ASSERT(data); - - if ((length < 5) || (length > BSON_MAX_SIZE)) { - return false; - } - - const uint32_t hdr_len = mlib_read_u32le(data); - - if (hdr_len != length) { - return false; - } - - if (data[length - 1]) { - return false; - } - - impl->flags = BSON_FLAG_NO_FREE_OBJECT | BSON_FLAG_RDONLY; - impl->len = (uint32_t)length; - impl->parent = NULL; - impl->depth = 0; - impl->indirect_buffer = NULL; - impl->indirect_buflen = NULL; - impl->offset = 0; - impl->own_buffer = (uint8_t *)data; - impl->own_buflen = length; - impl->realloc = NULL; - impl->realloc_func_ctx = NULL; - - return true; -} - - -bson_t * -bson_new(void) -{ - bson_impl_inline_t *impl; - bson_t *bson; - - bson = BSON_ALIGNED_ALLOC(bson_t); - - impl = (bson_impl_inline_t *)bson; - impl->flags = BSON_FLAG_INLINE_DATA; - impl->len = 5; - impl->data[0] = 5; - impl->data[1] = 0; - impl->data[2] = 0; - impl->data[3] = 0; - impl->data[4] = 0; - - return bson; -} - - -bson_t * -bson_sized_new(size_t size) -{ - bson_impl_alloc_t *impl_a; - bson_t *b; - - BSON_ASSERT(size <= BSON_MAX_SIZE); - - { - b = BSON_ALIGNED_ALLOC(bson_t); - impl_a = (bson_impl_alloc_t *)b; - } - - if (size <= BSON_INLINE_DATA_SIZE) { - bson_init(b); - b->flags &= ~BSON_FLAG_NO_FREE_OBJECT; - } else { - impl_a->flags = BSON_FLAG_NONE; - impl_a->len = 5; - impl_a->parent = NULL; - impl_a->depth = 0; - impl_a->indirect_buffer = NULL; - impl_a->indirect_buflen = NULL; - impl_a->offset = 0; - impl_a->own_buflen = BSON_MAX(5, size); - impl_a->own_buffer = bson_malloc(impl_a->own_buflen); - impl_a->own_buffer[0] = 5; - impl_a->own_buffer[1] = 0; - impl_a->own_buffer[2] = 0; - impl_a->own_buffer[3] = 0; - impl_a->own_buffer[4] = 0; - impl_a->realloc = bson_realloc_ctx; - impl_a->realloc_func_ctx = NULL; - } - - return b; -} - - -bson_t * -bson_new_from_data(const uint8_t *data, size_t length) -{ - BSON_ASSERT(data); - - if ((length < 5) || (length > BSON_MAX_SIZE) || data[length - 1]) { - // Invalid length, or not null-terminated - return NULL; - } - - const int32_t hdr = mlib_read_i32le(data); - - if (mlib_cmp(hdr, !=, length)) { - // Header's declared length is not equal to the length of the data buffer we were given - return NULL; - } - - bson_t *const bson = bson_sized_new(length); - memcpy(_bson_data(bson), data, length); - bson->len = (uint32_t)length; - - return bson; -} - - -bson_t * -bson_new_from_buffer(uint8_t **buf, size_t *buf_len, bson_realloc_func realloc_func, void *realloc_func_ctx) -{ - bson_impl_alloc_t *impl; - uint32_t length; - bson_t *bson; - - BSON_ASSERT(buf); - BSON_ASSERT(buf_len); - - if (!realloc_func) { - realloc_func = bson_realloc_ctx; - } - - bson = BSON_ALIGNED_ALLOC0(bson_t); - impl = (bson_impl_alloc_t *)bson; - - if (!*buf) { - length = 5; - *buf_len = 5; - *buf = realloc_func(*buf, *buf_len, realloc_func_ctx); - mlib_write_u32le(*buf, length); - (*buf)[4] = '\0'; - } else { - if ((*buf_len < 5) || (*buf_len > BSON_MAX_SIZE)) { - bson_free(bson); - return NULL; - } - length = mlib_read_u32le(*buf); - if (length < 5 || length > *buf_len) { - bson_free(bson); - return NULL; - } - } - - if ((*buf)[length - 1]) { - bson_free(bson); - return NULL; - } - - impl->flags = BSON_FLAG_NO_FREE_DATA; - impl->len = length; - impl->indirect_buffer = buf; - impl->indirect_buflen = buf_len; - impl->realloc = realloc_func; - impl->realloc_func_ctx = realloc_func_ctx; - - return bson; -} - - -bson_t * -bson_copy(const bson_t *bson) -{ - const uint8_t *data; - - BSON_ASSERT(bson); - - data = _bson_data(bson); - return bson_new_from_data(data, bson->len); -} - - -void -bson_copy_to(const bson_t *src, bson_t *dst) -{ - const uint8_t *data; - bson_impl_alloc_t *adst; - size_t len; - - BSON_ASSERT(src); - BSON_ASSERT(dst); - - if ((src->flags & BSON_FLAG_INLINE_DATA)) { - memcpy(dst, src, sizeof *dst); - dst->flags = (BSON_FLAG_NO_FREE_OBJECT | BSON_FLAG_INLINE_DATA); - return; - } - - data = _bson_data(src); - len = _bson_round_up_alloc_size((size_t)src->len); - MONGOC_DEBUG_ASSERT(len <= BSON_MAX_SIZE); - - adst = (bson_impl_alloc_t *)dst; - adst->flags = BSON_FLAG_NO_FREE_OBJECT; - adst->len = src->len; - adst->parent = NULL; - adst->depth = 0; - adst->indirect_buffer = NULL; - adst->indirect_buflen = NULL; - adst->offset = 0; - adst->own_buffer = bson_malloc(len); - adst->own_buflen = len; - adst->realloc = bson_realloc_ctx; - adst->realloc_func_ctx = NULL; - memcpy(adst->own_buffer, data, src->len); -} - - -static bool -should_ignore(const char *first_exclude, va_list args, const char *name) -{ - bool ret = false; - const char *exclude = first_exclude; - va_list args_copy; - - va_copy(args_copy, args); - - do { - if (!strcmp(name, exclude)) { - ret = true; - break; - } - } while ((exclude = va_arg(args_copy, const char *))); - - va_end(args_copy); - - return ret; -} - - -void -bson_copy_to_excluding_noinit_va(const bson_t *src, bson_t *dst, const char *first_exclude, va_list args) -{ - bson_iter_t iter; - - if (bson_iter_init(&iter, src)) { - while (bson_iter_next(&iter)) { - if (!should_ignore(first_exclude, args, bson_iter_key(&iter))) { - if (!bson_append_iter(dst, NULL, 0, &iter)) { - /* - * This should not be able to happen since we are copying - * from within a valid bson_t. - */ - BSON_ASSERT(false); - return; - } - } - } - } -} - - -void -bson_copy_to_excluding_noinit(const bson_t *src, bson_t *dst, const char *first_exclude, ...) -{ - va_list args; - - BSON_ASSERT(src); - BSON_ASSERT(dst); - BSON_ASSERT(first_exclude); - - va_start(args, first_exclude); - bson_copy_to_excluding_noinit_va(src, dst, first_exclude, args); - va_end(args); -} - -void -bson_destroy(bson_t *bson) -{ - if (!bson) { - return; - } - - if (!(bson->flags & (BSON_FLAG_RDONLY | BSON_FLAG_INLINE_DATA | BSON_FLAG_NO_FREE_DATA))) { - bson_impl_alloc_t *const a = (bson_impl_alloc_t *)bson; - if (a->indirect_buffer) { - bson_free(*a->indirect_buffer); - } else { - bson_free(a->own_buffer); - } - } - - if (!(bson->flags & BSON_FLAG_NO_FREE_OBJECT)) { - bson_free(bson); - } -} - - -uint8_t * -bson_reserve_buffer(bson_t *bson, uint32_t total_size) -{ - if (bson->flags & (BSON_FLAG_CHILD | BSON_FLAG_IN_CHILD | BSON_FLAG_RDONLY)) { - return NULL; - } - - if (total_size > bson->len) { - if ((size_t)total_size > BSON_MAX_SIZE) { - return NULL; - } - - /* Note that the bson_t can also include space for parent or sibling documents (offset) and for trailing bytes - * (depth). These sizes will be considered by _bson_grow() but we can assume they are zero in documents without - * BSON_FLAG_CHILD or BSON_FLAG_IN_CHILD. If this is called on a document that's part of a bson_writer_t, it is - * correct to ignore offset: we set the size of the current document, leaving previous documents alone. */ - if (!_bson_grow(bson, total_size - bson->len)) { - // Will fail due to overflow or when reallocation is needed on a buffer that does not support it. - return NULL; - } - } - - if (bson->flags & BSON_FLAG_INLINE_DATA) { - /* bson_grow didn't spill over */ - ((bson_impl_inline_t *)bson)->len = total_size; - BSON_ASSERT(total_size <= BSON_INLINE_DATA_SIZE); - } else { - bson_impl_alloc_t *impl = (bson_impl_alloc_t *)bson; - impl->len = total_size; - const size_t sz = impl->indirect_buffer ? *impl->indirect_buflen : impl->own_buflen; - (void)sz; - BSON_ASSERT(impl->offset <= sz && sz - impl->offset >= (size_t)total_size); - } - - return _bson_data(bson); -} - - -bool -bson_steal(bson_t *dst, bson_t *src) -{ - bson_impl_inline_t *src_inline; - bson_impl_inline_t *dst_inline; - bson_impl_alloc_t *alloc; - - BSON_ASSERT(dst); - BSON_ASSERT(src); - - bson_init(dst); - - if (src->flags & (BSON_FLAG_CHILD | BSON_FLAG_IN_CHILD | BSON_FLAG_RDONLY)) { - return false; - } - - if (src->flags & BSON_FLAG_INLINE_DATA) { - src_inline = (bson_impl_inline_t *)src; - dst_inline = (bson_impl_inline_t *)dst; - dst_inline->len = src_inline->len; - memcpy(dst_inline->data, src_inline->data, sizeof src_inline->data); - - /* for consistency, src is always invalid after steal, even if inline */ - src->len = 0; - } else { - memcpy(dst, src, sizeof(bson_t)); - alloc = (bson_impl_alloc_t *)dst; - alloc->flags |= BSON_FLAG_NO_FREE_OBJECT; - alloc->indirect_buffer = NULL; - alloc->indirect_buflen = NULL; - } - - if (!(src->flags & BSON_FLAG_NO_FREE_OBJECT)) { - bson_free(src); - } else { - /* src is invalid after steal */ - src->len = 0; - } - - return true; -} - - -uint8_t * -bson_destroy_with_steal(bson_t *bson, bool steal, uint32_t *length) -{ - uint8_t *ret = NULL; - - BSON_ASSERT(bson); - - if (length) { - *length = bson->len; - } - - if (!steal) { - bson_destroy(bson); - return NULL; - } - - if ((bson->flags & (BSON_FLAG_CHILD | BSON_FLAG_IN_CHILD | BSON_FLAG_RDONLY))) { - /* Do nothing */ - } else if ((bson->flags & BSON_FLAG_INLINE_DATA)) { - bson_impl_inline_t *inl; - - inl = (bson_impl_inline_t *)bson; - ret = bson_malloc(bson->len); - memcpy(ret, inl->data, bson->len); - } else { - bson_impl_alloc_t *const alloc = (bson_impl_alloc_t *)bson; - ret = alloc->indirect_buffer ? *alloc->indirect_buffer : alloc->own_buffer; - if (alloc->indirect_buffer) { - *alloc->indirect_buffer = NULL; - } - alloc->own_buffer = NULL; - } - - bson_destroy(bson); - - return ret; -} - - -const uint8_t * -bson_get_data(const bson_t *bson) -{ - BSON_ASSERT(bson); - - return _bson_data(bson); -} - - -uint32_t -bson_count_keys(const bson_t *bson) -{ - uint32_t count = 0; - bson_iter_t iter; - - BSON_ASSERT(bson); - - if (bson_iter_init(&iter, bson)) { - while (bson_iter_next(&iter)) { - count++; - } - } - - return count; -} - - -bool -bson_has_field(const bson_t *bson, const char *key) -{ - bson_iter_t iter; - bson_iter_t child; - - BSON_ASSERT(bson); - BSON_ASSERT(key); - - if (NULL != strchr(key, '.')) { - return (bson_iter_init(&iter, bson) && bson_iter_find_descendant(&iter, key, &child)); - } - - return bson_iter_init_find(&iter, bson, key); -} - - -int -bson_compare(const bson_t *bson, const bson_t *other) -{ - const uint8_t *data1; - const uint8_t *data2; - size_t len1; - size_t len2; - int64_t ret; - - data1 = _bson_data(bson) + 4; - len1 = bson->len - 4; - - data2 = _bson_data(other) + 4; - len2 = other->len - 4; - - if (len1 == len2) { - return memcmp(data1, data2, len1); - } - - ret = memcmp(data1, data2, BSON_MIN(len1, len2)); - - if (ret == 0) { - ret = (int64_t)len1 - (int64_t)len2; - } - - return (ret < 0) ? -1 : (ret > 0); -} - - -bool -bson_equal(const bson_t *bson, const bson_t *other) -{ - return !bson_compare(bson, other); -} - - -char * -bson_as_json_with_opts(const bson_t *bson, size_t *length, const bson_json_opts_t *opts) -{ - BSON_ASSERT_PARAM(bson); - BSON_OPTIONAL_PARAM(length); - BSON_ASSERT_PARAM(opts); - - // Convert the API-specified max length into a literal byte count; max length is transformed from a special value - // (-1) to the maximum representable size. - int32_t limit_i32 = opts->max_len; - uint32_t limit_u32 = 0; - if (limit_i32 == BSON_MAX_LEN_UNLIMITED) { - limit_u32 = UINT32_MAX - 1u; - } else if (limit_i32 > 0) { - limit_u32 = (uint32_t)limit_i32; - } - - // Use the bson length as an initial buffer capacity guess - mcommon_string_append_t append; - mcommon_string_set_append_with_limit(mcommon_string_new_with_capacity("", 0, bson->len), &append, limit_u32); - - if (opts->is_outermost_array ? mcommon_json_append_bson_array(&append, bson, opts->mode, BSON_MAX_RECURSION) - : mcommon_json_append_bson_document(&append, bson, opts->mode, BSON_MAX_RECURSION)) { - if (length) { - *length = (size_t)mcommon_strlen_from_append(&append); - } - return mcommon_string_from_append_destroy_with_steal(&append); - } else { - if (length) { - *length = 0; - } - mcommon_string_from_append_destroy(&append); - return NULL; - } -} - - -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, false}; - return bson_as_json_with_opts(bson, length, &opts); -} - - -char * -bson_as_legacy_extended_json(const bson_t *bson, size_t *length) -{ - const bson_json_opts_t opts = {BSON_JSON_MODE_LEGACY, BSON_MAX_LEN_UNLIMITED, false}; - return bson_as_json_with_opts(bson, length, &opts); -} - - -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, false}; - return bson_as_json_with_opts(bson, length, &opts); -} - - -char * -bson_array_as_legacy_extended_json(const bson_t *bson, size_t *length) -{ - const bson_json_opts_t opts = {BSON_JSON_MODE_LEGACY, BSON_MAX_LEN_UNLIMITED, true}; - return bson_as_json_with_opts(bson, length, &opts); -} - - -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); -} - - -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); -} - - -bool -bson_validate(const bson_t *bson, bson_validate_flags_t flags, size_t *offset) -{ - return bson_validate_with_error_and_offset(bson, flags, offset, NULL); -} - - -bool -bson_validate_with_error(const bson_t *bson, bson_validate_flags_t flags, bson_error_t *error) -{ - return bson_validate_with_error_and_offset(bson, flags, NULL, error); -} - -bool -bson_validate_with_error_and_offset(const bson_t *bson, - bson_validate_flags_t flags, - size_t *offset, - bson_error_t *error) -{ - BSON_ASSERT_PARAM(bson); - BSON_OPTIONAL_PARAM(offset); - BSON_OPTIONAL_PARAM(error); - - size_t offset_local = 0; - if (!offset) { - offset = &offset_local; - } - bson_error_t error_local; - if (!error) { - error = &error_local; - } - - return _bson_validate_impl_v2(bson, flags, offset, error); -} - - -bool -bson_concat(bson_t *dst, const bson_t *src) -{ - BSON_ASSERT(dst); - BSON_ASSERT(src); - - if (!bson_empty(src)) { - BSON_APPEND_BYTES_LIST_DECLARE(args); - - BSON_APPEND_BYTES_ADD_ARGUMENT(args, _bson_data(src) + 4, src->len - 5u); - - BSON_APPEND_BYTES_APPLY_ARGUMENTS(dst, args); - } - - return true; - -append_failure: - return false; -} - -struct _bson_array_builder_t { - uint32_t index; - bson_t bson; -}; - -bson_array_builder_t * -bson_array_builder_new(void) -{ - bson_array_builder_t *bab = BSON_ALIGNED_ALLOC0(bson_array_builder_t); - bson_init(&bab->bson); - return bab; -} - -// `bson_array_builder_append_impl` generates the next key index, calls -// `append_fn`, and may update the tracked next index. -#define bson_array_builder_append_impl(append_fn, ...) \ - if (1) { \ - BSON_ASSERT_PARAM(bab); \ - const char *key; \ - char buf[16]; \ - size_t key_length = bson_uint32_to_string(bab->index, &key, buf, sizeof buf); \ - /* Expect enough room in `buf` for key string. UINT32_MAX is 10 digits. \ - * With the NULL terminator, 11 is expected maximum number of \ - * characters. */ \ - BSON_ASSERT(key_length < sizeof buf); \ - bool ok = append_fn(&bab->bson, key, (int)key_length, __VA_ARGS__); \ - if (ok) { \ - bab->index += 1; \ - } \ - return ok; \ - } else \ - (void)0 - -#define bson_array_builder_append_impl_noargs(append_fn) \ - if (1) { \ - BSON_ASSERT_PARAM(bab); \ - const char *key; \ - char buf[16]; \ - size_t key_length = bson_uint32_to_string(bab->index, &key, buf, sizeof buf); \ - /* Expect enough room in `buf` for key string. UINT32_MAX is 10 digits. \ - * With the NULL terminator, 11 is expected maximum number of \ - * characters. */ \ - BSON_ASSERT(key_length < sizeof buf); \ - bool ok = append_fn(&bab->bson, key, (int)key_length); \ - if (ok) { \ - bab->index += 1; \ - } \ - return ok; \ - } else \ - (void)0 - -bool -bson_array_builder_append_value(bson_array_builder_t *bab, const bson_value_t *value) -{ - bson_array_builder_append_impl(bson_append_value, value); -} - - -bool -bson_array_builder_append_array(bson_array_builder_t *bab, const bson_t *array) -{ - bson_array_builder_append_impl(bson_append_array, array); -} - - -bool -bson_array_builder_append_array_from_vector(bson_array_builder_t *bab, const bson_iter_t *iter) -{ - bson_array_builder_append_impl(bson_append_array_from_vector, iter); -} - - -bool -bson_array_builder_append_binary(bson_array_builder_t *bab, - bson_subtype_t subtype, - const uint8_t *binary, - uint32_t length) -{ - bson_array_builder_append_impl(bson_append_binary, subtype, binary, length); -} - - -bool -bson_array_builder_append_binary_uninit(bson_array_builder_t *bab, - bson_subtype_t subtype, - uint8_t **binary, - uint32_t length) -{ - bson_array_builder_append_impl(bson_append_binary_uninit, subtype, binary, length); -} - - -bool -bson_array_builder_append_bool(bson_array_builder_t *bab, bool value) -{ - bson_array_builder_append_impl(bson_append_bool, value); -} - - -bool -bson_array_builder_append_code(bson_array_builder_t *bab, const char *javascript) -{ - bson_array_builder_append_impl(bson_append_code, javascript); -} - - -bool -bson_array_builder_append_code_with_scope(bson_array_builder_t *bab, const char *javascript, const bson_t *scope) -{ - bson_array_builder_append_impl(bson_append_code_with_scope, javascript, scope); -} - - -bool -bson_array_builder_append_dbpointer(bson_array_builder_t *bab, const char *collection, const bson_oid_t *oid) -{ - bson_array_builder_append_impl(bson_append_dbpointer, collection, oid); -} - - -bool -bson_array_builder_append_double(bson_array_builder_t *bab, double value) -{ - bson_array_builder_append_impl(bson_append_double, value); -} - - -bool -bson_array_builder_append_document(bson_array_builder_t *bab, const bson_t *value) -{ - bson_array_builder_append_impl(bson_append_document, value); -} - - -bool -bson_array_builder_append_document_begin(bson_array_builder_t *bab, bson_t *child) -{ - bson_array_builder_append_impl(bson_append_document_begin, child); -} - - -bool -bson_array_builder_append_document_end(bson_array_builder_t *bab, bson_t *child) -{ - return bson_append_document_end(&bab->bson, child); -} - - -bool -bson_array_builder_append_int32(bson_array_builder_t *bab, int32_t value) -{ - bson_array_builder_append_impl(bson_append_int32, value); -} - - -bool -bson_array_builder_append_int64(bson_array_builder_t *bab, int64_t value) -{ - bson_array_builder_append_impl(bson_append_int64, value); -} - - -bool -bson_array_builder_append_decimal128(bson_array_builder_t *bab, const bson_decimal128_t *value) -{ - bson_array_builder_append_impl(bson_append_decimal128, value); -} - - -bool -bson_array_builder_append_iter(bson_array_builder_t *bab, const bson_iter_t *iter) -{ - bson_array_builder_append_impl(bson_append_iter, iter); -} - - -bool -bson_array_builder_append_minkey(bson_array_builder_t *bab) -{ - bson_array_builder_append_impl_noargs(bson_append_minkey); -} - - -bool -bson_array_builder_append_maxkey(bson_array_builder_t *bab) -{ - bson_array_builder_append_impl_noargs(bson_append_maxkey); -} - - -bool -bson_array_builder_append_null(bson_array_builder_t *bab) -{ - bson_array_builder_append_impl_noargs(bson_append_null); -} - - -bool -bson_array_builder_append_oid(bson_array_builder_t *bab, const bson_oid_t *oid) -{ - bson_array_builder_append_impl(bson_append_oid, oid); -} - - -bool -bson_array_builder_append_regex(bson_array_builder_t *bab, const char *regex, const char *options) -{ - bson_array_builder_append_impl(bson_append_regex, regex, options); -} - - -bool -bson_array_builder_append_regex_w_len(bson_array_builder_t *bab, - const char *regex, - int regex_length, - const char *options) -{ - bson_array_builder_append_impl(bson_append_regex_w_len, regex, regex_length, options); -} - - -bool -bson_array_builder_append_utf8(bson_array_builder_t *bab, const char *value, int length) -{ - bson_array_builder_append_impl(bson_append_utf8, value, length); -} - - -bool -bson_array_builder_append_symbol(bson_array_builder_t *bab, const char *value, int length) -{ - bson_array_builder_append_impl(bson_append_symbol, value, length); -} - - -bool -bson_array_builder_append_time_t(bson_array_builder_t *bab, time_t value) -{ - bson_array_builder_append_impl(bson_append_time_t, value); -} - - -bool -bson_array_builder_append_timeval(bson_array_builder_t *bab, struct timeval *value) -{ - bson_array_builder_append_impl(bson_append_timeval, value); -} - - -bool -bson_array_builder_append_date_time(bson_array_builder_t *bab, int64_t value) -{ - bson_array_builder_append_impl(bson_append_date_time, value); -} - - -bool -bson_array_builder_append_now_utc(bson_array_builder_t *bab) -{ - bson_array_builder_append_impl_noargs(bson_append_now_utc); -} - - -bool -bson_array_builder_append_timestamp(bson_array_builder_t *bab, uint32_t timestamp, uint32_t increment) -{ - bson_array_builder_append_impl(bson_append_timestamp, timestamp, increment); -} - - -bool -bson_array_builder_append_undefined(bson_array_builder_t *bab) -{ - bson_array_builder_append_impl_noargs(bson_append_undefined); -} - - -bool -bson_array_builder_append_array_builder_begin(bson_array_builder_t *bab, bson_array_builder_t **child) -{ - bson_array_builder_append_impl(bson_append_array_builder_begin, child); -} - -bool -bson_array_builder_append_array_builder_end(bson_array_builder_t *bab, bson_array_builder_t *child) -{ - return bson_append_array_builder_end(&bab->bson, child); -} - - -bool -bson_array_builder_build(bson_array_builder_t *bab, bson_t *out) -{ - BSON_ASSERT_PARAM(bab); - BSON_ASSERT_PARAM(out); - if (!bson_steal(out, &bab->bson)) { - return false; - } - bson_init(&bab->bson); - bab->index = 0; - return true; -} - -void -bson_array_builder_destroy(bson_array_builder_t *bab) -{ - if (!bab) { - return; - } - bson_destroy(&bab->bson); - bson_free(bab); -} - -bool -bson_append_array_builder_begin(bson_t *bson, const char *key, int key_length, bson_array_builder_t **child) -{ - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(key); - BSON_ASSERT_PARAM(child); - *child = bson_array_builder_new(); - bool ok = bson_append_array_unsafe_begin(bson, key, key_length, &(*child)->bson); - if (!ok) { - bson_array_builder_destroy(*child); - *child = NULL; - } - return ok; -} - -bool -bson_append_array_builder_end(bson_t *bson, bson_array_builder_t *child) -{ - bool ok = bson_append_array_end(bson, &child->bson); - bson_array_builder_destroy(child); - return ok; -} diff --git a/bsonjs/bson/bson.h b/bsonjs/bson/bson.h deleted file mode 100644 index 924ca69..0000000 --- a/bsonjs/bson/bson.h +++ /dev/null @@ -1,1274 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef BSON_H -#define BSON_H - -#define BSON_INSIDE - -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export - -#include -#include - -#undef BSON_INSIDE - - -BSON_BEGIN_DECLS - - -/** - * bson_empty: - * @b: a bson_t. - * - * Checks to see if @b is an empty BSON document. An empty BSON document is - * a 5 byte document which contains the length (4 bytes) and a single NUL - * byte indicating end of fields. - */ -#define bson_empty(b) (((b)->len == 5) || !bson_get_data((b))[4]) - - -/** - * bson_empty0: - * - * Like bson_empty() but treats NULL the same as an empty bson_t document. - */ -#define bson_empty0(b) (!(b) || bson_empty(b)) - - -/** - * bson_clear: - * - * Easily free a bson document and set it to NULL. Use like: - * - * bson_t *doc = bson_new(); - * bson_clear (&doc); - * BSON_ASSERT (doc == NULL); - */ -#define bson_clear(bptr) \ - do { \ - if (*(bptr)) { \ - bson_destroy(*(bptr)); \ - *(bptr) = NULL; \ - } \ - } while (0) - - -/** - * BSON_MAX_SIZE: - * - * The maximum size in bytes of a BSON document. - */ -#define BSON_MAX_SIZE ((size_t)((1U << 31) - 1)) - - -#define BSON_APPEND_ARRAY(b, key, val) bson_append_array(b, key, (int)strlen(key), val) - -#define BSON_APPEND_ARRAY_BEGIN(b, key, child) bson_append_array_begin(b, key, (int)strlen(key), child) - -#define BSON_APPEND_BINARY(b, key, subtype, val, len) bson_append_binary(b, key, (int)strlen(key), subtype, val, len) - -#define BSON_APPEND_BOOL(b, key, val) bson_append_bool(b, key, (int)strlen(key), val) - -#define BSON_APPEND_CODE(b, key, val) bson_append_code(b, key, (int)strlen(key), val) - -#define BSON_APPEND_CODE_WITH_SCOPE(b, key, val, scope) \ - bson_append_code_with_scope(b, key, (int)strlen(key), val, scope) - -#define BSON_APPEND_DBPOINTER(b, key, coll, oid) bson_append_dbpointer(b, key, (int)strlen(key), coll, oid) - -#define BSON_APPEND_DOCUMENT_BEGIN(b, key, child) bson_append_document_begin(b, key, (int)strlen(key), child) - -#define BSON_APPEND_DOUBLE(b, key, val) bson_append_double(b, key, (int)strlen(key), val) - -#define BSON_APPEND_DOCUMENT(b, key, val) bson_append_document(b, key, (int)strlen(key), val) - -#define BSON_APPEND_INT32(b, key, val) bson_append_int32(b, key, (int)strlen(key), val) - -#define BSON_APPEND_INT64(b, key, val) bson_append_int64(b, key, (int)strlen(key), val) - -#define BSON_APPEND_MINKEY(b, key) bson_append_minkey(b, key, (int)strlen(key)) - -#define BSON_APPEND_DECIMAL128(b, key, val) bson_append_decimal128(b, key, (int)strlen(key), val) - -#define BSON_APPEND_MAXKEY(b, key) bson_append_maxkey(b, key, (int)strlen(key)) - -#define BSON_APPEND_NULL(b, key) bson_append_null(b, key, (int)strlen(key)) - -#define BSON_APPEND_OID(b, key, val) bson_append_oid(b, key, (int)strlen(key), val) - -#define BSON_APPEND_REGEX(b, key, val, opt) bson_append_regex(b, key, (int)strlen(key), val, opt) - -#define BSON_APPEND_UTF8(b, key, val) bson_append_utf8(b, key, (int)strlen(key), val, (int)strlen(val)) - -#define BSON_APPEND_SYMBOL(b, key, val) bson_append_symbol(b, key, (int)strlen(key), val, (int)strlen(val)) - -#define BSON_APPEND_TIME_T(b, key, val) bson_append_time_t(b, key, (int)strlen(key), val) - -#define BSON_APPEND_TIMEVAL(b, key, val) bson_append_timeval(b, key, (int)strlen(key), val) - -#define BSON_APPEND_DATE_TIME(b, key, val) bson_append_date_time(b, key, (int)strlen(key), val) - -#define BSON_APPEND_TIMESTAMP(b, key, val, inc) bson_append_timestamp(b, key, (int)strlen(key), val, inc) - -#define BSON_APPEND_UNDEFINED(b, key) bson_append_undefined(b, key, (int)strlen(key)) - -#define BSON_APPEND_VALUE(b, key, val) bson_append_value(b, key, (int)strlen(key), (val)) - - -/** - * bson_new: - * - * Allocates a new bson_t structure. Call the various bson_append_*() - * functions to add fields to the bson. You can iterate the bson_t at any - * time using a bson_iter_t and bson_iter_init(). - * - * Returns: A newly allocated bson_t that should be freed with bson_destroy(). - */ -BSON_EXPORT(bson_t *) bson_new(void); - - -BSON_EXPORT(bson_t *) -bson_new_from_json(const uint8_t *data, ssize_t len, bson_error_t *error); - - -BSON_EXPORT(bool) -bson_init_from_json(bson_t *bson, const char *data, ssize_t len, bson_error_t *error); - - -/** - * bson_init_static: - * @b: A pointer to a bson_t. - * @data: The data buffer to use. - * @length: The length of @data. - * - * Initializes a bson_t using @data and @length. This is ideal if you would - * like to use a stack allocation for your bson and do not need to grow the - * buffer. @data must be valid for the life of @b. - * - * Returns: true if initialized successfully; otherwise false. - */ -BSON_EXPORT(bool) -bson_init_static(bson_t *b, const uint8_t *data, size_t length); - - -/** - * bson_init: - * @b: A pointer to a bson_t. - * - * Initializes a bson_t for use. This function is useful to those that want a - * stack allocated bson_t. The usefulness of a stack allocated bson_t is - * marginal as the target buffer for content will still require heap - * allocations. It can help reduce heap fragmentation on allocators that do - * not employ SLAB/magazine semantics. - * - * You must call bson_destroy() with @b to release resources when you are done - * using @b. - */ -BSON_EXPORT(void) -bson_init(bson_t *b); - - -/** - * bson_reinit: - * @b: (inout): A bson_t. - * - * This is equivalent to calling bson_destroy() and bson_init() on a #bson_t. - * However, it will try to persist the existing malloc'd buffer if one exists. - * This is useful in cases where you want to reduce malloc overhead while - * building many documents. - */ -BSON_EXPORT(void) -bson_reinit(bson_t *b); - - -/** - * bson_new_from_data: - * @data: A buffer containing a serialized bson document. - * @length: The length of the document in bytes. - * - * Creates a new bson_t structure using the data provided. @data should contain - * at least @length bytes that can be copied into the new bson_t structure. - * - * Returns: A newly allocated bson_t that should be freed with bson_destroy(). - * If the first four bytes (little-endian) of data do not match @length, - * then NULL will be returned. - */ -BSON_EXPORT(bson_t *) -bson_new_from_data(const uint8_t *data, size_t length); - - -/** - * bson_new_from_buffer: - * @buf: A pointer to a buffer containing a serialized bson document. - * @buf_len: The length of the buffer in bytes. - * @realloc_fun: a realloc like function - * @realloc_fun_ctx: a context for the realloc function - * - * Creates a new bson_t structure using the data provided. @buf should contain - * a bson document, or null pointer should be passed for new allocations. - * - * Returns: A newly allocated bson_t that should be freed with bson_destroy(). - * The underlying buffer will be used and not be freed in destroy. - */ -BSON_EXPORT(bson_t *) -bson_new_from_buffer(uint8_t **buf, size_t *buf_len, bson_realloc_func realloc_func, void *realloc_func_ctx); - - -/** - * bson_sized_new: - * @size: A size_t containing the number of bytes to allocate. - * - * This will allocate a new bson_t with enough bytes to hold a buffer - * sized @size. @size must be smaller than INT_MAX bytes. - * - * Returns: A newly allocated bson_t that should be freed with bson_destroy(). - */ -BSON_EXPORT(bson_t *) -bson_sized_new(size_t size); - - -/** - * bson_copy: - * @bson: A bson_t. - * - * Copies @bson into a newly allocated bson_t. You must call bson_destroy() - * when you are done with the resulting value to free its resources. - * - * Returns: A newly allocated bson_t that should be free'd with bson_destroy() - */ -BSON_EXPORT(bson_t *) -bson_copy(const bson_t *bson); - - -/** - * bson_copy_to: - * @src: The source bson_t. - * @dst: The destination bson_t. - * - * Initializes @dst and copies the content from @src into @dst. - */ -BSON_EXPORT(void) -bson_copy_to(const bson_t *src, bson_t *dst); - -/** - * bson_copy_to_excluding_noinit: - * @src: A bson_t. - * @dst: A bson_t to initialize and copy into. - * @first_exclude: First field name to exclude. - * - * Does not call bson_init() on the dst. - */ -BSON_EXPORT(void) -bson_copy_to_excluding_noinit(const bson_t *src, bson_t *dst, const char *first_exclude, ...) BSON_GNUC_NULL_TERMINATED; - -BSON_EXPORT(void) -bson_copy_to_excluding_noinit_va(const bson_t *src, bson_t *dst, const char *first_exclude, va_list args); - - -/** - * bson_destroy: - * @bson: A bson_t. - * - * Frees the resources associated with @bson. - */ -BSON_EXPORT(void) -bson_destroy(bson_t *bson); - -BSON_EXPORT(uint8_t *) -bson_reserve_buffer(bson_t *bson, uint32_t total_size); - -BSON_EXPORT(bool) -bson_steal(bson_t *dst, bson_t *src); - - -/** - * bson_destroy_with_steal: - * @bson: A #bson_t. - * @steal: If ownership of the data buffer should be transferred to caller. - * @length: (out): location for the length of the buffer. - * - * Destroys @bson similar to calling bson_destroy() except that the underlying - * buffer will be returned and ownership transferred to the caller if @steal - * is non-zero. - * - * If length is non-NULL, the length of @bson will be stored in @length. - * - * It is a programming error to call this function with any bson that has - * been initialized static, or is being used to create a subdocument with - * functions such as bson_append_document_begin() or bson_append_array_begin(). - * - * Returns: a buffer owned by the caller if @steal is true. Otherwise NULL. - * If there was an error, NULL is returned. - */ -BSON_EXPORT(uint8_t *) -bson_destroy_with_steal(bson_t *bson, bool steal, uint32_t *length); - - -/** - * bson_get_data: - * @bson: A bson_t. - * - * Fetched the data buffer for @bson of @bson->len bytes in length. - * - * Returns: A buffer that should not be modified or freed. - */ -BSON_EXPORT(const uint8_t *) -bson_get_data(const bson_t *bson); - - -/** - * bson_count_keys: - * @bson: A bson_t. - * - * Counts the number of elements found in @bson. - */ -BSON_EXPORT(uint32_t) -bson_count_keys(const bson_t *bson); - - -/** - * bson_has_field: - * @bson: A bson_t. - * @key: The key to lookup. - * - * Checks to see if @bson contains a field named @key. - * - * This function is case-sensitive. - * - * Returns: true if @key exists in @bson; otherwise false. - */ -BSON_EXPORT(bool) -bson_has_field(const bson_t *bson, const char *key); - - -/** - * bson_compare: - * @bson: A bson_t. - * @other: A bson_t. - * - * Compares @bson to @other in a qsort() style comparison. - * See qsort() for information on how this function works. - * - * Returns: Less than zero, zero, or greater than zero. - */ -BSON_EXPORT(int) -bson_compare(const bson_t *bson, const bson_t *other); - -/* - * bson_equal: - * @bson: A bson_t. - * @other: A bson_t. - * - * Checks to see if @bson and @other are equal. - * - * Returns: true if equal; otherwise false. - */ -BSON_EXPORT(bool) -bson_equal(const bson_t *bson, const bson_t *other); - - -/** - * bson_validate: - * @bson: A bson_t. - * @offset: A location for the error offset. - * - * Validates a BSON document by walking through the document and inspecting - * the fields for valid content. - * - * Returns: true if @bson is valid; otherwise false and @offset is set. - */ -BSON_EXPORT(bool) -bson_validate(const bson_t *bson, bson_validate_flags_t flags, size_t *offset); - - -/** - * bson_validate_with_error: - * @bson: A bson_t. - * @error: A location for the error info. - * - * Validates a BSON document by walking through the document and inspecting - * the fields for valid content. - * - * Returns: true if @bson is valid; otherwise false and @error is filled out. - */ -BSON_EXPORT(bool) -bson_validate_with_error(const bson_t *bson, bson_validate_flags_t flags, bson_error_t *error); - - -/** - * bson_validate_with_error_and_offset: - * @bson: A bson_t. - * @offset: A location for the error offset. - * @error: A location for the error info. - * - * Validates a BSON document by walking through the document and inspecting - * the fields for valid content. - * - * Returns: true if @bson is valid; otherwise false, @offset is set - * and @error is filled out. - */ -BSON_EXPORT(bool) -bson_validate_with_error_and_offset(const bson_t *bson, - bson_validate_flags_t flags, - size_t *offset, - bson_error_t *error); - - -/** - * bson_as_json_with_opts: - * @bson: A bson_t. - * @length: A location for the string length, or NULL. - * @opts: A bson_t_json_opts_t defining options for the conversion - * - * Creates a new string containing @bson in the selected JSON format, - * conforming to the MongoDB Extended JSON Spec: - * - * github.com/mongodb/specifications/blob/master/source/extended-json.rst - * - * 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 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(). - */ -BSON_EXPORT(char *) -bson_as_json_with_opts(const bson_t *bson, size_t *length, const bson_json_opts_t *opts); - - -/** - * bson_as_canonical_extended_json: - * @bson: A bson_t. - * @length: A location for the string length, or NULL. - * - * Creates a new string containing @bson in canonical extended JSON format, - * conforming to the MongoDB Extended JSON Spec: - * - * github.com/mongodb/specifications/blob/master/source/extended-json.rst - * - * 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 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(). - */ -BSON_EXPORT(char *) -bson_as_canonical_extended_json(const bson_t *bson, size_t *length); - - -/** - * bson_as_legacy_extended_json: - * @bson: A bson_t. - * @length: A location for the string length, or NULL. - * - * Creates a new string containing @bson in libbson's legacy JSON format. - * Superseded by bson_as_canonical_extended_json and - * bson_as_relaxed_extended_json. 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. - * - * Returns: A newly allocated string that should be freed with bson_free(). - */ - -BSON_EXPORT(char *) -bson_as_legacy_extended_json(const bson_t *bson, size_t *length); - - -/** - * bson_as_relaxed_extended_json: - * @bson: A bson_t. - * @length: A location for the string length, or NULL. - * - * Creates a new string containing @bson in relaxed extended JSON format, - * conforming to the MongoDB Extended JSON Spec: - * - * github.com/mongodb/specifications/blob/master/source/extended-json.rst - * - * 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 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(). - */ -BSON_EXPORT(char *) -bson_as_relaxed_extended_json(const bson_t *bson, size_t *length); - - -/* like bson_as_legacy_extended_json() but for outermost arrays. */ -BSON_EXPORT(char *) -bson_array_as_legacy_extended_json(const bson_t *bson, size_t *length); - - -/* like bson_as_relaxed_extended_json() but for outermost arrays. */ -BSON_EXPORT(char *) -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_array_builder_t defines an API for building arrays. -// BSON arrays require sequential numeric keys "0", "1", "2", ... -typedef struct _bson_array_builder_t bson_array_builder_t; - -// bson_array_builder_new may be used to build a top-level BSON array. Example: -// `[1,2,3]`. -// To append an array field to a document (Example: `{ "field": [1,2,3] }`), use -// `bson_append_array_builder_begin`. -BSON_EXPORT(bson_array_builder_t *) bson_array_builder_new(void); - -// bson_array_builder_build initializes and moves BSON data to `out`. -// `bab` may be reused and will start appending a new array at index "0". -BSON_EXPORT(bool) -bson_array_builder_build(bson_array_builder_t *bab, bson_t *out); - -BSON_EXPORT(void) -bson_array_builder_destroy(bson_array_builder_t *bab); - -BSON_EXPORT(bool) -bson_append_value(bson_t *bson, const char *key, int key_length, const bson_value_t *value); - -#define BSON_APPEND_VALUE(b, key, val) bson_append_value(b, key, (int)strlen(key), (val)) - -BSON_EXPORT(bool) -bson_array_builder_append_value(bson_array_builder_t *bab, const bson_value_t *value); - -/** - * bson_append_array: - * @bson: A bson_t. - * @key: The key for the field. - * @array: A bson_t containing the array. - * - * Appends a BSON array to @bson. BSON arrays are like documents where the - * key is the string version of the index. For example, the first item of the - * array would have the key "0". The second item would have the index "1". - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_array(bson_t *bson, const char *key, int key_length, const bson_t *array); - -#define BSON_APPEND_ARRAY(b, key, val) bson_append_array(b, key, (int)strlen(key), val) - -BSON_EXPORT(bool) -bson_array_builder_append_array(bson_array_builder_t *bab, const bson_t *array); - -/** - * bson_append_array_from_vector: - * @bson: A bson_t that will be modified. - * @key: The key for the field. - * @iter: A bson_iter_t pointing to any supported vector in another bson_t. - * - * If @iter points to a supported vector type, converts the vector to a BSON array appended to @bson. - * - * Returns: true if successful; false if append would overflow max size or @iter does not point to a vector in a - * supported format. - */ -BSON_EXPORT(bool) -bson_append_array_from_vector(bson_t *bson, const char *key, int key_length, const bson_iter_t *iter); - -#define BSON_APPEND_ARRAY_FROM_VECTOR(b, key, iter) bson_append_array_from_vector(b, key, (int)strlen(key), iter) - -BSON_EXPORT(bool) -bson_array_builder_append_array_from_vector(bson_array_builder_t *bab, const bson_iter_t *iter); - -/** - * bson_append_binary: - * @bson: A bson_t. - * @key: The key for the field. - * @key_length: Optional length of 'key' in bytes, or -1 to use strlen(key). - * @subtype: The bson_subtype_t of the binary item. - * @binary: The binary buffer to append. - * @length: The length of @binary. - * - * Appends a binary buffer to the BSON document. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_binary( - bson_t *bson, const char *key, int key_length, bson_subtype_t subtype, const uint8_t *binary, uint32_t length); - -#define BSON_APPEND_BINARY(b, key, subtype, val, len) bson_append_binary(b, key, (int)strlen(key), subtype, val, len) - -BSON_EXPORT(bool) -bson_array_builder_append_binary(bson_array_builder_t *bab, - bson_subtype_t subtype, - const uint8_t *binary, - uint32_t length); - -/** - * bson_append_binary_uninit: - * @bson: A bson_t. - * @key: The key for the field. - * @key_length: Optional length of 'key' in bytes, or -1 to use strlen(key). - * @binary: Output parameter, pointer for the binary data within bson_t to be written. - * @length: Length of the binary field to allocate, in bytes. - * - * Returns: true if successful; false if append would overflow max size. - */ - -BSON_EXPORT(bool) -bson_append_binary_uninit( - bson_t *bson, const char *key, int key_length, bson_subtype_t subtype, uint8_t **binary, uint32_t length); - -#define BSON_APPEND_BINARY_UNINIT(b, key, subtype, val, len) \ - bson_append_binary_uninit(b, key, (int)strlen(key), subtype, val, len) - -BSON_EXPORT(bool) -bson_array_builder_append_binary_uninit(bson_array_builder_t *bab, - bson_subtype_t subtype, - uint8_t **binary, - uint32_t length); - -/** - * bson_append_bool: - * @bson: A bson_t. - * @key: The key for the field. - * @value: The boolean value. - * - * Appends a new field to @bson of type BSON_TYPE_BOOL. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_bool(bson_t *bson, const char *key, int key_length, bool value); - -#define BSON_APPEND_BOOL(b, key, val) bson_append_bool(b, key, (int)strlen(key), val) - -BSON_EXPORT(bool) -bson_array_builder_append_bool(bson_array_builder_t *bab, bool value); - -/** - * bson_append_code: - * @bson: A bson_t. - * @key: The key for the document. - * @javascript: JavaScript code to be executed. - * - * Appends a field of type BSON_TYPE_CODE to the BSON document. @javascript - * should contain a script in javascript to be executed. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_code(bson_t *bson, const char *key, int key_length, const char *javascript); - -#define BSON_APPEND_CODE(b, key, val) bson_append_code(b, key, (int)strlen(key), val) - -BSON_EXPORT(bool) -bson_array_builder_append_code(bson_array_builder_t *bab, const char *javascript); - -/** - * bson_append_code_with_scope: - * @bson: A bson_t. - * @key: The key for the document. - * @javascript: JavaScript code to be executed. - * @scope: A bson_t containing the scope for @javascript. - * - * Appends a field of type BSON_TYPE_CODEWSCOPE to the BSON document. - * @javascript should contain a script in javascript to be executed. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_code_with_scope(bson_t *bson, const char *key, int key_length, const char *javascript, const bson_t *scope); - -#define BSON_APPEND_CODE_WITH_SCOPE(b, key, val, scope) \ - bson_append_code_with_scope(b, key, (int)strlen(key), val, scope) - -BSON_EXPORT(bool) -bson_array_builder_append_code_with_scope(bson_array_builder_t *bab, const char *javascript, const bson_t *scope); - -/** - * bson_append_dbpointer: - * @bson: A bson_t. - * @key: The key for the field. - * @collection: The collection name. - * @oid: The oid to the reference. - * - * Appends a new field of type BSON_TYPE_DBPOINTER. This datum type is - * deprecated in the BSON spec and should not be used in new code. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_dbpointer(bson_t *bson, const char *key, int key_length, const char *collection, const bson_oid_t *oid); - -#define BSON_APPEND_DBPOINTER(b, key, coll, oid) bson_append_dbpointer(b, key, (int)strlen(key), coll, oid) - -BSON_EXPORT(bool) -bson_array_builder_append_dbpointer(bson_array_builder_t *bab, const char *collection, const bson_oid_t *oid); - -/** - * bson_append_double: - * @bson: A bson_t. - * @key: The key for the field. - * - * Appends a new field to @bson of the type BSON_TYPE_DOUBLE. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_double(bson_t *bson, const char *key, int key_length, double value); - -#define BSON_APPEND_DOUBLE(b, key, val) bson_append_double(b, key, (int)strlen(key), val) - -BSON_EXPORT(bool) -bson_array_builder_append_double(bson_array_builder_t *bab, double value); - -/** - * bson_append_document: - * @bson: A bson_t. - * @key: The key for the field. - * @value: A bson_t containing the subdocument. - * - * Appends a new field to @bson of the type BSON_TYPE_DOCUMENT. - * The documents contents will be copied into @bson. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_document(bson_t *bson, const char *key, int key_length, const bson_t *value); - -#define BSON_APPEND_DOCUMENT(b, key, val) bson_append_document(b, key, (int)strlen(key), val) - -BSON_EXPORT(bool) -bson_array_builder_append_document(bson_array_builder_t *bab, const bson_t *value); - -/** - * bson_append_document_begin: - * @bson: A bson_t. - * @key: The key for the field. - * @key_length: The length of @key in bytes not including NUL or -1 - * if @key_length is NUL terminated. - * @child: A location to an uninitialized bson_t. - * - * Appends a new field named @key to @bson. The field is, however, - * incomplete. @child will be initialized so that you may add fields to the - * child document. Child will use a memory buffer owned by @bson and - * therefore grow the parent buffer as additional space is used. This allows - * a single malloc'd buffer to be used when building documents which can help - * reduce memory fragmentation. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_document_begin(bson_t *bson, const char *key, int key_length, bson_t *child); - -#define BSON_APPEND_DOCUMENT_BEGIN(b, key, child) bson_append_document_begin(b, key, (int)strlen(key), child) - -BSON_EXPORT(bool) -bson_array_builder_append_document_begin(bson_array_builder_t *bab, bson_t *child); - -/** - * bson_append_document_end: - * @bson: A bson_t. - * @child: A bson_t supplied to bson_append_document_begin(). - * - * Finishes the appending of a document to a @bson. @child is considered - * disposed after this call and should not be used any further. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_document_end(bson_t *bson, bson_t *child); - -BSON_EXPORT(bool) -bson_array_builder_append_document_end(bson_array_builder_t *bab, bson_t *child); - - -/** - * bson_append_array_begin: - * @bson: A bson_t. - * @key: The key for the field. - * @key_length: The length of @key in bytes not including NUL or -1 - * if @key_length is NUL terminated. - * @child: A location to an uninitialized bson_t. - * @deprecated Use bson_append_array_builder_begin or bson_append_array_unsafe_begin instead. - * - * Appends a new field named @key to @bson. The field is, however, - * incomplete. @child will be initialized so that you may add fields to the - * child array. Child will use a memory buffer owned by @bson and - * therefore grow the parent buffer as additional space is used. This allows - * a single malloc'd buffer to be used when building arrays which can help - * reduce memory fragmentation. - * - * The type of @child will be BSON_TYPE_ARRAY and therefore the keys inside - * of it MUST be "0", "1", etc. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_GNUC_DEPRECATED_FOR(bson_append_array_builder_begin or bson_append_array_unsafe_begin) -BSON_EXPORT(bool) -bson_append_array_begin(bson_t *bson, const char *key, int key_length, bson_t *child); - -#define BSON_APPEND_ARRAY_BEGIN(b, key, child) bson_append_array_begin(b, key, (int)strlen(key), child) - -BSON_EXPORT(bool) -bson_append_array_unsafe_begin(bson_t *bson, const char *key, int key_length, bson_t *child); - -#define BSON_APPEND_ARRAY_UNSAFE_BEGIN(b, key, child) bson_append_array_unsafe_begin(b, key, (int)strlen(key), child) - -/** - * bson_append_array_end: - * @bson: A bson_t. - * @child: A bson_t supplied to bson_append_array_begin(). - * - * Finishes the appending of a array to a @bson. @child is considered - * disposed after this call and should not be used any further. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_array_end(bson_t *bson, bson_t *child); - - -/** - * bson_append_int32: - * @bson: A bson_t. - * @key: The key for the field. - * @value: The int32_t 32-bit integer value. - * - * Appends a new field of type BSON_TYPE_INT32 to @bson. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_int32(bson_t *bson, const char *key, int key_length, int32_t value); - -#define BSON_APPEND_INT32(b, key, val) bson_append_int32(b, key, (int)strlen(key), val) - -BSON_EXPORT(bool) -bson_array_builder_append_int32(bson_array_builder_t *bab, int32_t value); - -/** - * bson_append_int64: - * @bson: A bson_t. - * @key: The key for the field. - * @value: The int64_t 64-bit integer value. - * - * Appends a new field of type BSON_TYPE_INT64 to @bson. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_int64(bson_t *bson, const char *key, int key_length, int64_t value); - -#define BSON_APPEND_INT64(b, key, val) bson_append_int64(b, key, (int)strlen(key), val) - -BSON_EXPORT(bool) -bson_array_builder_append_int64(bson_array_builder_t *bab, int64_t value); - -/** - * bson_append_decimal128: - * @bson: A bson_t. - * @key: The key for the field. - * @value: The bson_decimal128_t decimal128 value. - * - * Appends a new field of type BSON_TYPE_DECIMAL128 to @bson. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_decimal128(bson_t *bson, const char *key, int key_length, const bson_decimal128_t *value); - -#define BSON_APPEND_DECIMAL128(b, key, val) bson_append_decimal128(b, key, (int)strlen(key), val) - -BSON_EXPORT(bool) -bson_array_builder_append_decimal128(bson_array_builder_t *bab, const bson_decimal128_t *value); - -/** - * bson_append_iter: - * @bson: A bson_t to append to. - * @key: The key name or %NULL to take current key from @iter. - * @key_length: The key length or -1 to use strlen(). - * @iter: The iter located on the position of the element to append. - * - * Appends a new field to @bson that is equivalent to the field currently - * pointed to by @iter. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_iter(bson_t *bson, const char *key, int key_length, const bson_iter_t *iter); - -#define BSON_APPEND_ITER(b, key, val) bson_append_iter(b, key, (int)strlen(key), val) - -BSON_EXPORT(bool) -bson_array_builder_append_iter(bson_array_builder_t *bab, const bson_iter_t *iter); - -/** - * bson_append_minkey: - * @bson: A bson_t. - * @key: The key for the field. - * - * Appends a new field of type BSON_TYPE_MINKEY to @bson. This is a special - * type that compares lower than all other possible BSON element values. - * - * See http://bsonspec.org for more information on this type. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_minkey(bson_t *bson, const char *key, int key_length); - -#define BSON_APPEND_MINKEY(b, key) bson_append_minkey(b, key, (int)strlen(key)) - -BSON_EXPORT(bool) -bson_array_builder_append_minkey(bson_array_builder_t *bab); - -/** - * bson_append_maxkey: - * @bson: A bson_t. - * @key: The key for the field. - * - * Appends a new field of type BSON_TYPE_MAXKEY to @bson. This is a special - * type that compares higher than all other possible BSON element values. - * - * See http://bsonspec.org for more information on this type. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_maxkey(bson_t *bson, const char *key, int key_length); - -#define BSON_APPEND_MAXKEY(b, key) bson_append_maxkey(b, key, (int)strlen(key)) - -BSON_EXPORT(bool) -bson_array_builder_append_maxkey(bson_array_builder_t *bab); - -/** - * bson_append_null: - * @bson: A bson_t. - * @key: The key for the field. - * - * Appends a new field to @bson with NULL for the value. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_null(bson_t *bson, const char *key, int key_length); - -#define BSON_APPEND_NULL(b, key) bson_append_null(b, key, (int)strlen(key)) - -BSON_EXPORT(bool) -bson_array_builder_append_null(bson_array_builder_t *bab); - -/** - * bson_append_oid: - * @bson: A bson_t. - * @key: The key for the field. - * @oid: bson_oid_t. - * - * Appends a new field to the @bson of type BSON_TYPE_OID using the contents of - * @oid. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_oid(bson_t *bson, const char *key, int key_length, const bson_oid_t *oid); - -#define BSON_APPEND_OID(b, key, val) bson_append_oid(b, key, (int)strlen(key), val) - -BSON_EXPORT(bool) -bson_array_builder_append_oid(bson_array_builder_t *bab, const bson_oid_t *oid); - -/** - * bson_append_regex: - * @bson: A bson_t. - * @key: The key of the field. - * @regex: The regex to append to the bson. - * @options: Options for @regex. - * - * Appends a new field to @bson of type BSON_TYPE_REGEX. @regex should - * be the regex string. @options should contain the options for the regex. - * - * Valid options for @options are: - * - * 'i' for case-insensitive. - * 'm' for multiple matching. - * 'x' for verbose mode. - * 'l' to make \w and \W locale dependent. - * 's' for dotall mode ('.' matches everything) - * 'u' to make \w and \W match unicode. - * - * For more detailed information about BSON regex elements, see bsonspec.org. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_regex(bson_t *bson, const char *key, int key_length, const char *regex, const char *options); - -#define BSON_APPEND_REGEX(b, key, val, opt) bson_append_regex(b, key, (int)strlen(key), val, opt) - -BSON_EXPORT(bool) -bson_array_builder_append_regex(bson_array_builder_t *bab, const char *regex, const char *options); - -/** - * bson_append_regex: - * @bson: A bson_t. - * @key: The key of the field. - * @key_length: The length of the key string. - * @regex: The regex to append to the bson. - * @regex_length: The length of the regex string. - * @options: Options for @regex. - * - * Appends a new field to @bson of type BSON_TYPE_REGEX. @regex should - * be the regex string. @options should contain the options for the regex. - * - * Valid options for @options are: - * - * 'i' for case-insensitive. - * 'm' for multiple matching. - * 'x' for verbose mode. - * 'l' to make \w and \W locale dependent. - * 's' for dotall mode ('.' matches everything) - * 'u' to make \w and \W match unicode. - * - * For more detailed information about BSON regex elements, see bsonspec.org. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_regex_w_len( - bson_t *bson, const char *key, int key_length, const char *regex, int regex_length, const char *options); - -BSON_EXPORT(bool) -bson_array_builder_append_regex_w_len(bson_array_builder_t *bab, - const char *regex, - int regex_length, - const char *options); - -/** - * bson_append_utf8: - * @bson: A bson_t. - * @key: The key for the field. - * @value: A UTF-8 encoded string. - * @length: The length of @value or -1 if it is NUL terminated. - * - * Appends a new field to @bson using @key as the key and @value as the UTF-8 - * encoded value. - * - * It is the callers responsibility to ensure @value is valid UTF-8. You can - * use bson_utf8_validate() to perform this check. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_utf8(bson_t *bson, const char *key, int key_length, const char *value, int length); - -#define BSON_APPEND_UTF8(b, key, val) bson_append_utf8(b, key, (int)strlen(key), val, (int)strlen(val)) - -BSON_EXPORT(bool) -bson_array_builder_append_utf8(bson_array_builder_t *bab, const char *value, int length); - -#define BSON_APPEND_ARRAY_BUILDER_APPEND_UTF8(b, value) bson_array_builder_append_utf8(b, value, (int)strlen(value)) - -/** - * bson_append_symbol: - * @bson: A bson_t. - * @key: The key for the field. - * @value: The symbol as a string. - * @length: The length of @value or -1 if NUL-terminated. - * - * Appends a new field to @bson of type BSON_TYPE_SYMBOL. This BSON type is - * deprecated and should not be used in new code. - * - * See http://bsonspec.org for more information on this type. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_symbol(bson_t *bson, const char *key, int key_length, const char *value, int length); - -#define BSON_APPEND_SYMBOL(b, key, val) bson_append_symbol(b, key, (int)strlen(key), val, (int)strlen(val)) - -BSON_EXPORT(bool) -bson_array_builder_append_symbol(bson_array_builder_t *bab, const char *value, int length); - -#define BSON_APPEND_ARRAY_BUILDER_APPEND_SYMBOL(b, value) bson_array_builder_append_symbol(b, value, (int)strlen(value)) - -/** - * bson_append_time_t: - * @bson: A bson_t. - * @key: The key for the field. - * @value: A time_t. - * - * Appends a BSON_TYPE_DATE_TIME field to @bson using the time_t @value for the - * number of seconds since UNIX epoch in UTC. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_time_t(bson_t *bson, const char *key, int key_length, time_t value); - -#define BSON_APPEND_TIME_T(b, key, val) bson_append_time_t(b, key, (int)strlen(key), val) - -BSON_EXPORT(bool) -bson_array_builder_append_time_t(bson_array_builder_t *bab, time_t value); - -/** - * bson_append_timeval: - * @bson: A bson_t. - * @key: The key for the field. - * @value: A struct timeval containing the date and time. - * - * Appends a BSON_TYPE_DATE_TIME field to @bson using the struct timeval - * provided. The time is persisted in milliseconds since the UNIX epoch in UTC. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_timeval(bson_t *bson, const char *key, int key_length, struct timeval *value); - -#define BSON_APPEND_TIMEVAL(b, key, val) bson_append_timeval(b, key, (int)strlen(key), val) - -BSON_EXPORT(bool) -bson_array_builder_append_timeval(bson_array_builder_t *bab, struct timeval *value); - -/** - * bson_append_date_time: - * @bson: A bson_t. - * @key: The key for the field. - * @key_length: The length of @key in bytes or -1 if \0 terminated. - * @value: The number of milliseconds elapsed since UNIX epoch. - * - * Appends a new field to @bson of type BSON_TYPE_DATE_TIME. - * - * Returns: true if successful; otherwise false. - */ -BSON_EXPORT(bool) -bson_append_date_time(bson_t *bson, const char *key, int key_length, int64_t value); - -#define BSON_APPEND_DATE_TIME(b, key, val) bson_append_date_time(b, key, (int)strlen(key), val) - -BSON_EXPORT(bool) -bson_array_builder_append_date_time(bson_array_builder_t *bab, int64_t value); - -/** - * bson_append_now_utc: - * @bson: A bson_t. - * @key: The key for the field. - * @key_length: The length of @key or -1 if it is NULL terminated. - * - * Appends a BSON_TYPE_DATE_TIME field to @bson using the current time in UTC - * as the field value. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_now_utc(bson_t *bson, const char *key, int key_length); - -#define BSON_APPEND_NOW_UTC(b, key) bson_append_now_utc(b, key, (int)strlen(key)) - -BSON_EXPORT(bool) -bson_array_builder_append_now_utc(bson_array_builder_t *bab); - -/** - * bson_append_timestamp: - * @bson: A bson_t. - * @key: The key for the field. - * @timestamp: 4 byte timestamp. - * @increment: 4 byte increment for timestamp. - * - * Appends a field of type BSON_TYPE_TIMESTAMP to @bson. This is a special type - * used by MongoDB replication and sharding. If you need generic time and date - * fields use bson_append_time_t() or bson_append_timeval(). - * - * Setting @increment and @timestamp to zero has special semantics. See - * http://bsonspec.org for more information on this field type. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_timestamp(bson_t *bson, const char *key, int key_length, uint32_t timestamp, uint32_t increment); - -#define BSON_APPEND_TIMESTAMP(b, key, val, inc) bson_append_timestamp(b, key, (int)strlen(key), val, inc) - -BSON_EXPORT(bool) -bson_array_builder_append_timestamp(bson_array_builder_t *bab, uint32_t timestamp, uint32_t increment); - -/** - * bson_append_undefined: - * @bson: A bson_t. - * @key: The key for the field. - * - * Appends a field of type BSON_TYPE_UNDEFINED. This type is deprecated in the - * spec and should not be used for new code. However, it is provided for those - * needing to interact with legacy systems. - * - * Returns: true if successful; false if append would overflow max size. - */ -BSON_EXPORT(bool) -bson_append_undefined(bson_t *bson, const char *key, int key_length); - -#define BSON_APPEND_UNDEFINED(b, key) bson_append_undefined(b, key, (int)strlen(key)) - -BSON_EXPORT(bool) -bson_array_builder_append_undefined(bson_array_builder_t *bab); - -BSON_EXPORT(bool) -bson_concat(bson_t *dst, const bson_t *src); - -BSON_EXPORT(bool) -bson_append_array_builder_begin(bson_t *bson, const char *key, int key_length, bson_array_builder_t **child); - -#define BSON_APPEND_ARRAY_BUILDER_BEGIN(b, key, child) bson_append_array_builder_begin(b, key, (int)strlen(key), child) - -BSON_EXPORT(bool) -bson_array_builder_append_array_builder_begin(bson_array_builder_t *bab, bson_array_builder_t **child); - -BSON_EXPORT(bool) -bson_append_array_builder_end(bson_t *bson, bson_array_builder_t *child); - -BSON_EXPORT(bool) -bson_array_builder_append_array_builder_end(bson_array_builder_t *bab, bson_array_builder_t *child); - - -BSON_END_DECLS - - -#endif /* BSON_H */ diff --git a/bsonjs/bson/bson_t-private.h b/bsonjs/bson/bson_t-private.h deleted file mode 100644 index 99ddf73..0000000 --- a/bsonjs/bson/bson_t-private.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef BSON_PRIVATE_H -#define BSON_PRIVATE_H - -#include // IWYU pragma: export - -// - -#include -#include -#include - - -BSON_BEGIN_DECLS - - -typedef enum { - BSON_FLAG_NONE = 0, - BSON_FLAG_INLINE_DATA = (1 << 0), // Set if BSON data is embedded in `bson_t`. - BSON_FLAG_NO_FREE_OBJECT = (1 << 1), // Set if `bson_destroy` should not free `bson_t` object. - BSON_FLAG_RDONLY = (1 << 2), - BSON_FLAG_CHILD = (1 << 3), - BSON_FLAG_IN_CHILD = (1 << 4), - BSON_FLAG_NO_FREE_DATA = (1 << 5), // Set if `bson_destroy` should not free BSON data. -} bson_flags_t; - - -#define BSON_INLINE_DATA_SIZE 120 - - -BSON_ALIGNED_BEGIN(BSON_ALIGN_OF_PTR) -typedef struct { - bson_flags_t flags; - uint32_t len; - uint8_t data[BSON_INLINE_DATA_SIZE]; -} bson_impl_inline_t BSON_ALIGNED_END(BSON_ALIGN_OF_PTR); - - -BSON_STATIC_ASSERT2(impl_inline_t, sizeof(bson_impl_inline_t) == 128); - -typedef struct { - bson_flags_t flags; /* flags describing the bson_t */ - /* len is part of the public bson_t declaration. It is not - * exposed through an accessor function. Plus, it's redundant since - * BSON self describes the length in the first four bytes of the - * buffer. */ - uint32_t len; /* length of bson document in bytes */ - /** - * @brief Pointer to a parent document object if we are a child of some other - * document, otherwise a null pointer. - */ - bson_t *parent; - uint32_t depth; /* Subdocument depth. */ - /** - * @brief If non-null, this pointer refers to the pointer to a buffer that is not directly owned - * by this `bson_t`, but may still manipulated/managed by this `bson_t`. - * - * If this pointer is null, then the data buffer is in `own_buffer` - */ - uint8_t **indirect_buffer; - size_t *indirect_buflen; - /** - * @brief The offset (in bytes) to the beginning of the document within the data buffer. - */ - size_t offset; - /** - * @brief Data buffer that is managed directly by this `bson_t`. This is not used if `indirect_buffer` - * is non-null. - */ - uint8_t *own_buffer; - size_t own_buflen; - bson_realloc_func realloc; /* our realloc implementation */ - void *realloc_func_ctx; /* context for our realloc func */ -} bson_impl_alloc_t; - - -BSON_STATIC_ASSERT2(impl_alloc_t, sizeof(bson_impl_alloc_t) <= 128); - -// Ensure both `bson_t` implementations have the same alignment requirement: -BSON_STATIC_ASSERT2(impls_match_alignment, BSON_ALIGNOF(bson_impl_inline_t) == BSON_ALIGNOF(bson_impl_alloc_t)); -// Ensure `bson_t` has same alignment requirement as implementations: -BSON_STATIC_ASSERT2(impls_match_alignment, BSON_ALIGNOF(bson_t) == BSON_ALIGNOF(bson_impl_alloc_t)); - - -BSON_END_DECLS - - -#endif /* BSON_PRIVATE_H */ diff --git a/bsonjs/bson/bson_t.h b/bsonjs/bson/bson_t.h deleted file mode 100644 index bd0d045..0000000 --- a/bsonjs/bson/bson_t.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BSON_BSON_T_H_INCLUDED -#define BSON_BSON_T_H_INCLUDED - -#include - -#include - -/** - * bson_t: - * - * This structure manages a buffer whose contents are a properly formatted - * BSON document. You may perform various transforms on the BSON documents. - * Additionally, it can be iterated over using bson_iter_t. - * - * See bson_iter_init() for iterating the contents of a bson_t. - * - * When building a bson_t structure using the various append functions, - * memory allocations may occur. That is performed using power of two - * allocations and realloc(). - * - * See http://bsonspec.org for the BSON document spec. - * - * This structure is meant to fit in two sequential 64-byte cachelines. - */ -BSON_ALIGNED_BEGIN(BSON_ALIGN_OF_PTR) 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. */ -} bson_t BSON_ALIGNED_END(BSON_ALIGN_OF_PTR); - -/** - * BSON_INITIALIZER: - * - * This macro can be used to initialize a #bson_t structure on the stack - * without calling bson_init(). - * - * |[ - * bson_t b = BSON_INITIALIZER; - * ]| - */ -#define BSON_INITIALIZER {3, 5, {5}} - -BSON_STATIC_ASSERT2(bson_t, sizeof(bson_t) == 128); - -#endif // BSON_BSON_T_H_INCLUDED diff --git a/bsonjs/bson/compat.h b/bsonjs/bson/compat.h deleted file mode 100644 index 8fea74c..0000000 --- a/bsonjs/bson/compat.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BSON_COMPAT_H -#define BSON_COMPAT_H - - -#if defined(__MINGW32__) -#if defined(__USE_MINGW_ANSI_STDIO) -#if __USE_MINGW_ANSI_STDIO < 1 -#error "__USE_MINGW_ANSI_STDIO > 0 is required for correct PRI* macros" -#endif -#else -#define __USE_MINGW_ANSI_STDIO 1 -#endif -#endif - -#include // IWYU pragma: export -#include // IWYU pragma: export - - -#ifdef BSON_OS_WIN32 -#if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x0601) -#undef _WIN32_WINNT -#endif -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0601 -#endif -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include // IWYU pragma: export -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#include // IWYU pragma: export -#undef WIN32_LEAN_AND_MEAN -#else -#include // IWYU pragma: export -#endif -#include // IWYU pragma: export -#include // IWYU pragma: export -#endif - - -#ifdef BSON_OS_UNIX -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#endif - - -#include - -#include // IWYU pragma: export -#include // IWYU pragma: export - -#include // IWYU pragma: keep: to be removed. -#include // IWYU pragma: keep: to be removed. -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: keep: to be removed. -#include // IWYU pragma: keep: to be removed. -#include // IWYU pragma: keep: to be removed. -#include // IWYU pragma: keep: to be removed. - - -BSON_BEGIN_DECLS - -#if !defined(_MSC_VER) || (_MSC_VER >= 1800) -#include // IWYU pragma: export -#endif -#ifdef _MSC_VER -#ifndef __cplusplus -/* benign redefinition of type */ -#pragma warning(disable : 4142) -#ifndef _SSIZE_T_DEFINED -#define _SSIZE_T_DEFINED -typedef SSIZE_T ssize_t; -#endif -#ifndef _SIZE_T_DEFINED -#define _SIZE_T_DEFINED -typedef SIZE_T size_t; -#endif -#pragma warning(default : 4142) -#else -/* - * MSVC++ does not include ssize_t, just size_t. - * So we need to synthesize that as well. - */ -#pragma warning(disable : 4142) -#ifndef _SSIZE_T_DEFINED -#define _SSIZE_T_DEFINED -typedef SSIZE_T ssize_t; -#endif -#pragma warning(default : 4142) -#endif -#ifndef PRIi32 -#define PRIi32 "d" -#endif -#ifndef PRId32 -#define PRId32 "d" -#endif -#ifndef PRIu32 -#define PRIu32 "u" -#endif -#ifndef PRIi64 -#define PRIi64 "I64i" -#endif -#ifndef PRId64 -#define PRId64 "I64i" -#endif -#ifndef PRIu64 -#define PRIu64 "I64u" -#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; -#endif - - -#if !defined(va_copy) && defined(__va_copy) -#define va_copy(dst, src) __va_copy(dst, src) -#endif - - -#if !defined(va_copy) -#define va_copy(dst, src) ((dst) = (src)) -#endif - - -#ifdef _MSC_VER -/** Expands the arguments if compiling with MSVC, otherwise empty */ -#define BSON_IF_MSVC(...) __VA_ARGS__ -/** Expands the arguments if compiling with GCC or Clang, otherwise empty */ -#define BSON_IF_GNU_LIKE(...) -#elif defined(__GNUC__) || defined(__clang__) -/** Expands the arguments if compiling with MSVC, otherwise empty */ -#define BSON_IF_MSVC(...) -/** Expands the arguments if compiling with GCC or Clang, otherwise empty */ -#define BSON_IF_GNU_LIKE(...) __VA_ARGS__ -#else -/** Unsupported compiler. **/ -#define BSON_IF_MSVC(...) -#define BSON_IF_GNU_LIKE(...) -#endif - -#ifdef BSON_OS_WIN32 -/** Expands the arguments if compiling for Windows, otherwise empty */ -#define BSON_IF_WINDOWS(...) __VA_ARGS__ -/** Expands the arguments if compiling for POSIX, otherwise empty */ -#define BSON_IF_POSIX(...) -#elif defined(BSON_OS_UNIX) -/** Expands the arguments if compiling for Windows, otherwise empty */ -#define BSON_IF_WINDOWS(...) -/** Expands the arguments if compiling for POSIX, otherwise empty */ -#define BSON_IF_POSIX(...) __VA_ARGS__ -#endif - - -BSON_END_DECLS - - -#endif /* BSON_COMPAT_H */ diff --git a/bsonjs/bson/config.h b/bsonjs/bson/config.h deleted file mode 100644 index bcb41f7..0000000 --- a/bsonjs/bson/config.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2018-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if !defined(BSON_INSIDE) && !defined(BSON_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef BSON_CONFIG_H -#define BSON_CONFIG_H - -#define PY_SSIZE_T_CLEAN /* Make "s#" use Py_ssize_t rather than int. */ - -/* - * Rely on CPython to make libbson portable - */ -#include - - -/* - * Define to 1234 for Little Endian, 4321 for Big Endian. - */ -#ifdef WORDS_BIGENDIAN -# define BSON_BYTE_ORDER 4321 -#else -# define BSON_BYTE_ORDER 1234 -#endif - - -/* - * Define to 1 if you have stdbool.h - */ -#define BSON_HAVE_STDBOOL_H 1 -#if BSON_HAVE_STDBOOL_H != 1 -# undef BSON_HAVE_STDBOOL_H -#endif - - -/* - * Define to 1 for POSIX-like systems, 2 for Windows. - */ -#ifdef MS_WINDOWS -# define BSON_OS 2 -#else -# define BSON_OS 1 -#endif - - -/* - * Define to 1 if you have clock_gettime() available. - */ -#ifdef HAVE_CLOCK_GETTIME -# define BSON_HAVE_CLOCK_GETTIME 1 -#endif - -#if BSON_HAVE_CLOCK_GETTIME != 1 -# undef BSON_HAVE_CLOCK_GETTIME -#endif - - -/* - * Define to 1 if you have strings.h available on your platform. - */ -#define BSON_HAVE_STRINGS_H 0 -#if BSON_HAVE_STRINGS_H != 1 -# undef BSON_HAVE_STRINGS_H -#endif - - -/* - * Define to 1 if you have strnlen available on your platform. - */ -#define BSON_HAVE_STRNLEN 0 -#if BSON_HAVE_STRNLEN != 1 -# undef BSON_HAVE_STRNLEN -#endif - - -/* - * Define to 1 if you have snprintf available on your platform. - */ -#ifdef MS_WINDOWS -# define BSON_HAVE_SNPRINTF 0 -#else -# define BSON_HAVE_SNPRINTF 1 -#endif - -#if BSON_HAVE_SNPRINTF != 1 -# undef BSON_HAVE_SNPRINTF -#endif - - -/* - * Define to 1 if you have gmtime_r available on your platform. - */ -#ifdef MS_WINDOWS -# define BSON_HAVE_GMTIME_R 0 -#else -# define BSON_HAVE_GMTIME_R 1 -#endif - -#if BSON_HAVE_GMTIME_R != 1 -# undef BSON_HAVE_GMTIME_R -#endif - - -/* - * Define to 1 if you have struct timespec available on your platform. - */ -#ifdef HAVE_CLOCK_GETTIME -# define BSON_HAVE_TIMESPEC 1 -#endif - -#if BSON_HAVE_TIMESPEC != 1 -# undef BSON_HAVE_TIMESPEC -#endif - - -#ifdef MS_WINDOWS -# define BSON_HAVE_RAND_R 0 -#else -# define BSON_HAVE_RAND_R 1 -#endif -#if BSON_HAVE_RAND_R != 1 -# undef BSON_HAVE_RAND_R -#endif - - -#define BSON_HAVE_STRLCPY 0 -#if BSON_HAVE_STRLCPY != 1 -# undef BSON_HAVE_STRLCPY -#endif - - -/* - * Define to 1 if you have aligned_alloc available on your platform. - */ -#ifdef MS_WINDOWS -# define BSON_HAVE_ALIGNED_ALLOC 0 -#else -# define BSON_HAVE_ALIGNED_ALLOC 1 -#endif -#if BSON_HAVE_ALIGNED_ALLOC != 1 -# undef BSON_HAVE_ALIGNED_ALLOC -#endif - -#endif /* BSON_CONFIG_H */ diff --git a/bsonjs/bson/error.c b/bsonjs/bson/error.c deleted file mode 100644 index 29c650b..0000000 --- a/bsonjs/bson/error.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include -#include - -#include -#include -#include -#include - -#include -#include - -// See `bson_strerror_r()` definition below. -#if !defined(_WIN32) && !defined(__APPLE__) -#include // uselocale() -#endif - - -/* - *-------------------------------------------------------------------------- - * - * bson_set_error -- - * - * Initializes @error using the parameters specified. - * - * @domain is an application specific error domain which should - * describe which module initiated the error. Think of this as the - * exception type. - * - * @code is the @domain specific error code. - * - * @format is used to generate the format string. It uses vsnprintf() - * internally so the format should match what you would use there. - * - * Parameters: - * @error: A #bson_error_t. - * @domain: The error domain. - * @code: The error code. - * @format: A printf style format string. - * - * Returns: - * None. - * - * Side effects: - * @error is initialized. - * - *-------------------------------------------------------------------------- - */ - -void -bson_set_error(bson_error_t *error, /* OUT */ - uint32_t domain, /* IN */ - uint32_t code, /* IN */ - const char *format, /* IN */ - ...) /* IN */ -{ - va_list args; - - if (error) { - error->domain = domain; - error->code = code; - bson_set_error_category(error, BSON_ERROR_CATEGORY); - - va_start(args, format); - char buffer[sizeof error->message]; - bson_vsnprintf(buffer, sizeof error->message, format, args); - memcpy(error->message, buffer, sizeof buffer); - va_end(args); - } -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_strerror_r -- - * - * This is a reentrant safe macro for strerror. - * - * The resulting string may be stored in @buf. - * - * Returns: - * A pointer to a static string or @buf. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -char * -bson_strerror_r(int err_code, /* IN */ - char *buf BSON_MAYBE_UNUSED, /* IN */ - size_t buflen BSON_MAYBE_UNUSED) /* IN */ -{ - static const char *unknown_msg = "Unknown error"; - char *ret = NULL; - -#if defined(_WIN32) - // Windows does not provide `strerror_l` or `strerror_r`, but it does - // unconditionally provide `strerror_s`. - if (strerror_s(buf, buflen, err_code) == 0) { - ret = buf; - } -#elif defined(_AIX) - // AIX does not provide strerror_l, and its strerror_r isn't glibc's. - // But it does provide a glibc compatible one called __linux_strerror_r - ret = __linux_strerror_r(err_code, buf, buflen); -#elif defined(__APPLE__) - // Apple does not provide `strerror_l`, but it does unconditionally provide - // the XSI-compliant `strerror_r`, but only when compiling with Apple Clang. - // GNU extensions may still be a problem if we are being compiled with GCC on - // Apple. Avoid the compatibility headaches with GNU extensions and the musl - // library by assuming the implementation will not cause UB when reading the - // error message string even when `strerror_r` fails, as encouraged (but not - // required) by the POSIX spec (see: - // https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html#tag_16_574_08). - (void)strerror_r(err_code, buf, buflen); - ret = buf; -#elif defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700 - // The behavior (of `strerror_l`) is undefined if the locale argument to - // `strerror_l()` is the special locale object LC_GLOBAL_LOCALE or is not a - // valid locale object handle. - locale_t locale = uselocale((locale_t)0); - // No need to test for error (it can only be [EINVAL]). - if (locale == LC_GLOBAL_LOCALE) { - // Only use our own locale if a thread-local locale was not already set. - // This is just to satisfy `strerror_l`. We do NOT want to unconditionally - // set a thread-local locale. - locale = newlocale(LC_MESSAGES_MASK, "C", (locale_t)0); - } - BSON_ASSERT(locale != LC_GLOBAL_LOCALE); - - // Avoid `strerror_r` compatibility headaches with GNU extensions and the - // musl library by using `strerror_l` instead. Furthermore, `strerror_r` is - // scheduled to be marked as obsolete in favor of `strerror_l` in the - // upcoming POSIX Issue 8 (see: - // https://www.austingroupbugs.net/view.php?id=655). - // - // POSIX Spec: since strerror_l() is required to return a string for some - // errors, an application wishing to check for all error situations should - // set errno to 0, then call strerror_l(), then check errno. - if (locale != (locale_t)0) { - errno = 0; - ret = strerror_l(err_code, locale); - - if (errno != 0) { - ret = NULL; - } - - freelocale(locale); - } else { - // Could not obtain a valid `locale_t` object to satisfy `strerror_l`. - // Fallback to `bson_strncpy` below. - } -#elif defined(_GNU_SOURCE) - // Unlikely, but continue supporting use of GNU extension in cases where the - // C Driver is being built without _XOPEN_SOURCE=700. - ret = strerror_r(err_code, buf, buflen); -#else -#error "Unable to find a supported strerror_r candidate" -#endif - - if (!ret) { - bson_strncpy(buf, unknown_msg, buflen); - ret = buf; - } - - return ret; -} diff --git a/bsonjs/bson/error.h b/bsonjs/bson/error.h deleted file mode 100644 index f31fa7b..0000000 --- a/bsonjs/bson/error.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BSON_ERROR_T_INCLUDED -#define BSON_ERROR_T_INCLUDED - -#include - -#include - -BSON_BEGIN_DECLS - -#define BSON_ERROR_BUFFER_SIZE 503 - -BSON_ALIGNED_BEGIN(BSON_ALIGN_OF_PTR) // Aligned for backwards-compatibility. -typedef struct _bson_error_t { - uint32_t domain; - uint32_t code; - char message[BSON_ERROR_BUFFER_SIZE]; - uint8_t reserved; // For internal use only! -} bson_error_t BSON_ALIGNED_END(BSON_ALIGN_OF_PTR); - - -BSON_STATIC_ASSERT2(error_t, sizeof(bson_error_t) == 512); - -#define BSON_ERROR_JSON 1 -#define BSON_ERROR_READER 2 -#define BSON_ERROR_INVALID 3 -#define BSON_ERROR_VECTOR 4 - -BSON_EXPORT(void) -bson_set_error(bson_error_t *error, uint32_t domain, uint32_t code, const char *format, ...) BSON_GNUC_PRINTF(4, 5); - -BSON_EXPORT(char *) -bson_strerror_r(int err_code, char *buf, size_t buflen); - -/** - * @brief Reset the content of a bson_error_t to indicate no error. - * - * @param error Pointer to an error to be overwritten. If null, this function - * has no effect. - * - * This is static-inline because it is trivially optimizable as a (conditional) - * `memset`. - */ -static inline void -bson_error_clear(bson_error_t *error) -{ - if (!error) { - return; - } - // Statically initialized to a zero struct: - static bson_error_t zero_error; - // Replace the caller's value: - *error = zero_error; -} - -/** - * @brief Given a `bson_error_t` pointer l-value, ensure that it is non-null, and clear any - * error value that it might hold. - * - * @param ErrorPointer An l-value expression of type `bson_error_t*`. - * - * If the passed pointer is null, then it will be updated to point to an anonymous - * `bson_error_t` object that lives in the caller's scope. - * - * @note This macro is not valid in C++ because it relies on C99 compound literal semantics - */ -#define bson_error_reset(ErrorPointer) bson_error_reset(&(ErrorPointer), &(bson_error_t){0}) -static inline void(bson_error_reset)(bson_error_t **error, bson_error_t *localptr) -{ - if (*error == NULL) { - *error = localptr; - } - bson_error_clear(*error); -} - -BSON_END_DECLS - - -#endif // BSON_ERROR_T_INCLUDED diff --git a/bsonjs/bson/macros.h b/bsonjs/bson/macros.h deleted file mode 100644 index b9c8480..0000000 --- a/bsonjs/bson/macros.h +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BSON_MACROS_H -#define BSON_MACROS_H - -#include // IWYU pragma: export - -#include -#include - -#ifdef __cplusplus -#include -#endif - - -#if BSON_OS == 1 -#define BSON_OS_UNIX -#elif BSON_OS == 2 -#define BSON_OS_WIN32 -#else -#error "Unknown operating system." -#endif - - -#ifdef __cplusplus -#define BSON_BEGIN_DECLS extern "C" { -#define BSON_END_DECLS } -#else -#define BSON_BEGIN_DECLS -#define BSON_END_DECLS -#endif - - -#if defined(__GNUC__) -#define BSON_GNUC_CHECK_VERSION(major, minor) \ - ((__GNUC__ > (major)) || ((__GNUC__ == (major)) && (__GNUC_MINOR__ >= (minor)))) -#else -#define BSON_GNUC_CHECK_VERSION(major, minor) 0 -#endif - - -#if defined(__GNUC__) -#define BSON_GNUC_IS_VERSION(major, minor) ((__GNUC__ == (major)) && (__GNUC_MINOR__ == (minor))) -#else -#define BSON_GNUC_IS_VERSION(major, minor) 0 -#endif - - -/* Decorate public functions: - * - if BSON_STATIC, we're compiling a static libbson or a program - * that uses libbson as a static library. Don't decorate functions. - * - else if BSON_COMPILATION, we're compiling a shared libbson, mark - * public functions for export from the shared lib - * - else, we're compiling a program that uses libbson as a shared library, - * mark public functions as DLL imports for Microsoft Visual C - */ - -#ifdef _MSC_VER -/* - * Microsoft Visual C - */ -#ifdef BSON_STATIC -#define BSON_API -#elif defined(BSON_COMPILATION) -#define BSON_API __declspec(dllexport) -#else -#define BSON_API __declspec(dllimport) -#endif -#define BSON_CALL __cdecl - -#elif defined(__GNUC__) -/* - * GCC - */ -#ifdef BSON_STATIC -#define BSON_API -#elif defined(BSON_COMPILATION) -#define BSON_API __attribute__((visibility("default"))) -#else -#define BSON_API -#endif -#define BSON_CALL - -#else -/* - * Other compilers - */ -#define BSON_API -#define BSON_CALL - -#endif - -#define BSON_EXPORT(type) BSON_API type BSON_CALL - - -#ifdef MIN -#define BSON_MIN MIN -#elif defined(__cplusplus) -#define BSON_MIN(a, b) ((std::min)(a, b)) -#elif defined(_MSC_VER) -#define BSON_MIN(a, b) ((a) < (b) ? (a) : (b)) -#else -#define BSON_MIN(a, b) (((a) < (b)) ? (a) : (b)) -#endif - - -#ifdef MAX -#define BSON_MAX MAX -#elif defined(__cplusplus) -#define BSON_MAX(a, b) ((std::max)(a, b)) -#elif defined(_MSC_VER) -#define BSON_MAX(a, b) ((a) > (b) ? (a) : (b)) -#else -#define BSON_MAX(a, b) (((a) > (b)) ? (a) : (b)) -#endif - - -#ifdef ABS -#define BSON_ABS ABS -#else -#define BSON_ABS(a) (((a) < 0) ? ((a) * -1) : (a)) -#endif - -#if defined(__cplusplus) && (__cplusplus >= 201103L || defined(_MSVC_LANG)) -#define BSON_ALIGNOF(expr) alignof(expr) -#elif defined(__STDC_VERSION__) && __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 (BSON_ALIGNOF(void *)) -#endif - -#if defined(_MSC_VER) -#define BSON_ALIGNED_BEGIN(_N) __declspec(align(BSON_ALIGN_OF_PTR)) -#define BSON_ALIGNED_END(_N) -#else -#define BSON_ALIGNED_BEGIN(_N) -#define BSON_ALIGNED_END(_N) __attribute__((aligned((_N) > BSON_ALIGN_OF_PTR ? BSON_ALIGN_OF_PTR : (_N)))) -#endif - - -#define bson_str_empty(s) (!s[0]) -#define bson_str_empty0(s) (!s || !s[0]) - - -#if defined(_MSC_VER) -#define BSON_FUNC __FUNCTION__ -#else -#define BSON_FUNC __func__ -#endif - - -#if defined(_MSC_VER) -#define BSON_INLINE __inline -#else -#define BSON_INLINE __inline__ -#endif - - -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L -#define BSON_NORETURN [[noreturn]] -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L -#define BSON_NORETURN _Noreturn -#elif defined(__GNUC__) && 2 < __GNUC__ + (8 <= __GNUC_MINOR__) -#define BSON_NORETURN __attribute__((__noreturn__)) -#else -#define BSON_NORETURN -#endif - - -#if defined(__GNUC__) -#define BSON_RESTRICT __restrict__ -#elif defined(_MSC_VER) -#define BSON_RESTRICT __restrict -#elif !defined(__cplusplus) -// C99 (not C++) -#define BSON_RESTRICT restrict -#else -#define BSON_RESTRICT -#endif - - -BSON_NORETURN static BSON_INLINE void -_bson_assert_failed_on_line(const char *file, int line, const char *func, const char *test) -{ - fprintf(stderr, "%s:%d %s(): assertion failed: %s\n", file, line, func, test); - abort(); -} - -BSON_NORETURN static BSON_INLINE void -_bson_assert_failed_on_param(const char *param, const char *func) -{ - fprintf(stderr, "The parameter: %s, in function %s, cannot be NULL\n", param, func); - abort(); -} - -#define BSON_ASSERT(test) \ - do { \ - if (!(BSON_LIKELY(test))) { \ - _bson_assert_failed_on_line(__FILE__, (int)(__LINE__), BSON_FUNC, #test); \ - } \ - } while (0) - -/** - * @brief Assert the expression `Assertion`, and evaluates to `Value` on - * success. - */ -#define BSON_ASSERT_INLINE(Assertion, Value) \ - ((void)((Assertion) ? (0) : (_bson_assert_failed_on_line(__FILE__, (int)(__LINE__), BSON_FUNC, #Assertion), 0)), \ - Value) - -/** - * @brief Assert that the given pointer is non-NULL, while also evaluating to - * that pointer. - * - * Can be used to inline assertions with a pointer dereference: - * - * ``` - * foo* f = get_foo(); - * bar* b = BSON_ASSERT_PTR_INLINE(f)->bar_value; - * ``` - */ -#define BSON_ASSERT_PTR_INLINE(Pointer) BSON_ASSERT_INLINE((Pointer) != NULL, (Pointer)) - -/* Used for asserting parameters to provide a more precise error message */ -#define BSON_ASSERT_PARAM(param) \ - do { \ - if ((BSON_UNLIKELY(param == NULL))) { \ - _bson_assert_failed_on_param(#param, BSON_FUNC); \ - } \ - } while (0) - -// `BSON_OPTIONAL_PARAM` is a documentation-only macro to document X may be NULL. -// Useful in combination with `BSON_ASSERT_PARAM` to document and assert pointer parameters. -#define BSON_OPTIONAL_PARAM(param) ((void)param) - -/* obsolete macros, preserved for compatibility */ -#define BSON_STATIC_ASSERT(s) BSON_STATIC_ASSERT_(s, __LINE__) -#define BSON_STATIC_ASSERT_JOIN(a, b) BSON_STATIC_ASSERT_JOIN2(a, b) -#define BSON_STATIC_ASSERT_JOIN2(a, b) a##b -#define BSON_STATIC_ASSERT_(s, l) typedef char BSON_STATIC_ASSERT_JOIN(static_assert_test_, __LINE__)[(s) ? 1 : -1] - -/* modern macros */ -#define BSON_STATIC_ASSERT2(_name, _s) BSON_STATIC_ASSERT2_(_s, __LINE__, _name) -#define BSON_STATIC_ASSERT_JOIN3(_a, _b, _name) BSON_STATIC_ASSERT_JOIN4(_a, _b, _name) -#define BSON_STATIC_ASSERT_JOIN4(_a, _b, _name) _a##_b##_name -#define BSON_STATIC_ASSERT2_(_s, _l, _name) \ - typedef char BSON_STATIC_ASSERT_JOIN3(static_assert_test_, __LINE__, _name)[(_s) ? 1 : -1] - - -#if defined(__GNUC__) -#define BSON_GNUC_PURE __attribute__((pure)) -#define BSON_GNUC_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#else -#define BSON_GNUC_PURE -#define BSON_GNUC_WARN_UNUSED_RESULT -#endif - - -#if BSON_GNUC_CHECK_VERSION(4, 0) && !defined(_WIN32) -#define BSON_GNUC_NULL_TERMINATED __attribute__((sentinel)) -#define BSON_GNUC_INTERNAL __attribute__((visibility("hidden"))) -#else -#define BSON_GNUC_NULL_TERMINATED -#define BSON_GNUC_INTERNAL -#endif - - -#if defined(__GNUC__) -#define BSON_LIKELY(x) __builtin_expect(!!(x), 1) -#define BSON_UNLIKELY(x) __builtin_expect(!!(x), 0) -#else -#define BSON_LIKELY(v) v -#define BSON_UNLIKELY(v) v -#endif - - -#if defined(__clang__) -#define BSON_GNUC_PRINTF(f, v) __attribute__((format(printf, f, v))) -#elif BSON_GNUC_CHECK_VERSION(4, 4) -#define BSON_GNUC_PRINTF(f, v) __attribute__((format(gnu_printf, f, v))) -#else -#define BSON_GNUC_PRINTF(f, v) -#endif - - -#if defined(__LP64__) || defined(_LP64) -#define BSON_WORD_SIZE 64 -#else -#define BSON_WORD_SIZE 32 -#endif - - -#ifdef _MSC_VER -#define BSON_ENSURE_ARRAY_PARAM_SIZE(_n) -#define BSON_TYPEOF decltype -#else -#define BSON_ENSURE_ARRAY_PARAM_SIZE(_n) static(_n) -#define BSON_TYPEOF typeof -#endif - -/** - * @brief Statically annotate an entity as deprecated, including the given deprecation message - * - * @param Message The message to be included in a deprecation warning. This - * should be a string literal. - */ -#define BSON_DEPRECATED(Message) _bsonDeprecatedImpl(Message) - -// Pick the appropriate implementation of a deprecation attribute -#if defined(_MSC_VER) -// For MSVC, emit __declspec(deprecated(Msg)) -#define _bsonDeprecatedImpl(Msg) __declspec(deprecated(Msg)) -#elif defined(__GNUC__) && (defined(__clang__) || BSON_GNUC_CHECK_VERSION(4, 5)) -// For new enough Clang and GCC, emit __attribute__((__deprecated__(Msg))) -#define _bsonDeprecatedImpl(Msg) __attribute__((__deprecated__(Msg))) -#elif defined(__GNUC__) -// For older GCC, emit deprecation attribute without the message -#define _bsonDeprecatedImpl(Msg) __attribute__((__deprecated__)) -#else -// For other compilers, emit nothing -#define _bsonDeprecatedImpl(Msg) -#endif - -#define BSON_DEPRECATED_FOR(F) BSON_DEPRECATED("This API is deprecated. Use " #F " instead.") - -#define BSON_GNUC_DEPRECATED BSON_DEPRECATED("This API is deprecated") -#define BSON_GNUC_DEPRECATED_FOR(F) BSON_DEPRECATED_FOR(F) - -#define BSON_CONCAT_IMPL(a, ...) a##__VA_ARGS__ -#define BSON_CONCAT(a, ...) BSON_CONCAT_IMPL(a, __VA_ARGS__) -#define BSON_CONCAT3(a, b, c) BSON_CONCAT(a, BSON_CONCAT(b, c)) -#define BSON_CONCAT4(a, b, c, d) BSON_CONCAT(BSON_CONCAT(a, b), BSON_CONCAT(c, d)) - - -/** - * @brief String-ify the given argument - */ -#define BSON_STR(...) #__VA_ARGS__ - -/** - * @brief Mark the attached declared entity as "possibly-unused." - * - * Does nothing on MSVC. - */ -#if defined(__GNUC__) || defined(__clang__) -#define BSON_MAYBE_UNUSED __attribute__((unused)) -#else -#define BSON_MAYBE_UNUSED /* Nothing for other compilers */ -#endif - -/** - * @brief Mark a point in the code as unreachable. If the point is reached, the - * program will abort with an error message. - * - * @param What A string to include in the error message if this point is ever - * executed. - */ -#define BSON_UNREACHABLE(What) \ - do { \ - fprintf(stderr, "%s:%d %s(): Unreachable code reached: %s\n", __FILE__, (int)(__LINE__), BSON_FUNC, What); \ - 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) - -// Disable the -Wunsafe-buffer-usage warning. -#define BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN -#define BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END -#if defined(__clang__) -#if __has_warning("-Wunsafe-buffer-usage") -#undef BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN -#undef BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END -#define BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN \ - _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wunsafe-buffer-usage\"") -#define BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END _Pragma("clang diagnostic pop") -#endif // __has_warning("-Wunsafe-buffer-usage") -#endif // defined(__clang__) - -#endif /* BSON_MACROS_H */ diff --git a/bsonjs/bson/memory.c b/bsonjs/bson/memory.c deleted file mode 100644 index 0d84ea5..0000000 --- a/bsonjs/bson/memory.c +++ /dev/null @@ -1,512 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include - -#include - -#include - -#include -#include - -#include -#include -#include -#include - -// 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(BSON_HAVE_ALIGNED_ALLOC) -{ - MC_DISABLE_IMPLICIT_WARNING_BEGIN - return aligned_alloc(alignment, num_bytes); - MC_DISABLE_IMPLICIT_WARNING_END -} -#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 - -mlib_diagnostic_push(); -mlib_msvc_warning(disable : 4232); -static bson_mem_vtable_t gMemVtable = {.malloc = malloc, - .calloc = calloc, - .realloc = realloc, - .free = free, - .aligned_alloc = _aligned_alloc_impl, - .padding = {0}}; -mlib_diagnostic_pop(); - - -/* - *-------------------------------------------------------------------------- - * - * bson_malloc -- - * - * Allocates @num_bytes of memory 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: - * @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_malloc(size_t num_bytes) /* IN */ -{ - void *mem = NULL; - - 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); - abort(); - } - } - - return mem; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_malloc0 -- - * - * Like bson_malloc() except the memory is zeroed first. This is - * similar to calloc() except that abort() is called in case of - * failure to allocate memory. - * - * Parameters: - * @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_malloc0(size_t num_bytes) /* IN */ -{ - void *mem = NULL; - - 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); - abort(); - } - } - - return mem; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_aligned_alloc -- - * - * 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; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_array_alloc -- - * - * Allocates memory for an array of objects. - * - * Libbson does not try to handle OOM conditions as it is beyond the - * scope of this library to handle so appropriately. - * - * Parameters: - * @num_elems: The number of objects to allocate. - * @elem_size: The size of each object in bytes. - * - * Returns: - * A pointer if successful; otherwise abort() is called and this - * function will never return. - * - *-------------------------------------------------------------------------- - */ - -void * -bson_array_alloc(size_t num_elems /* IN */, size_t elem_size /* IN */) -{ - void *mem = NULL; - size_t num_bytes = 0; - BSON_ASSERT(!mlib_mul(&num_bytes, num_elems, elem_size)); - - if (BSON_LIKELY(num_bytes)) { - mem = bson_malloc(num_bytes); - } - return mem; -} - -/* - *-------------------------------------------------------------------------- - * - * bson_array_alloc0-- - * - * Like bson_array_alloc() except the memory is zeroed after allocation - * for convenience. - * - * Parameters: - * @num_elems: The number of objects to allocate. - * @elem_size: The size of each object in bytes. - * - * Returns: - * A pointer if successful; otherwise abort() is called and this - * function will never return. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void * -bson_array_alloc0(size_t num_elems /* IN */, size_t elem_size /* IN */) -{ - void *mem = NULL; - size_t num_bytes = 0; - BSON_ASSERT(!mlib_mul(&num_bytes, num_elems, elem_size)); - - if (BSON_LIKELY(num_bytes)) { - if (BSON_UNLIKELY(!(mem = gMemVtable.calloc(num_elems, elem_size)))) { - fprintf(stderr, "Failure to allocate memory in bson_array_alloc0(). errno: %d.\n", errno); - abort(); - } - } - return mem; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_realloc -- - * - * This function behaves similar to realloc() except that if there is - * a failure abort() is called. - * - * Parameters: - * @mem: The memory to realloc, or NULL. - * @num_bytes: The size of the new allocation or 0 to free. - * - * Returns: - * The new allocation if successful; otherwise abort() is called and - * this function never returns. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void * -bson_realloc(void *mem, /* IN */ - size_t num_bytes) /* IN */ -{ - /* - * Not all platforms are guaranteed to free() the memory if a call to - * realloc() with a size of zero occurs. Windows, Linux, and FreeBSD do, - * however, OS X does not. - */ - if (BSON_UNLIKELY(num_bytes == 0)) { - gMemVtable.free(mem); - return NULL; - } - - mem = gMemVtable.realloc(mem, num_bytes); - - if (BSON_UNLIKELY(!mem)) { - fprintf(stderr, "Failure to re-allocate memory in bson_realloc(). errno: %d.\n", errno); - abort(); - } - - return mem; -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_realloc_ctx -- - * - * This wraps bson_realloc and provides a compatible api for similar - * functions with a context - * - * Parameters: - * @mem: The memory to realloc, or NULL. - * @num_bytes: The size of the new allocation or 0 to free. - * @ctx: Ignored - * - * Returns: - * The new allocation if successful; otherwise abort() is called and - * this function never returns. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - - -void * -bson_realloc_ctx(void *mem, /* IN */ - size_t num_bytes, /* IN */ - void *ctx) /* IN */ -{ - BSON_UNUSED(ctx); - - return bson_realloc(mem, num_bytes); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_free -- - * - * Frees @mem using the underlying allocator. - * - * Currently, this only calls free() directly, but that is subject to - * change. - * - * Parameters: - * @mem: An allocation to free. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_free(void *mem) /* IN */ -{ - gMemVtable.free(mem); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_zero_free -- - * - * Frees @mem using the underlying allocator. @size bytes of @mem will - * be zeroed before freeing the memory. This is useful in scenarios - * where @mem contains passwords or other sensitive information. - * - * Parameters: - * @mem: An allocation to free. - * @size: The number of bytes in @mem. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_zero_free(void *mem, /* IN */ - size_t size) /* IN */ -{ - if (BSON_LIKELY(mem)) { - memset(mem, 0, size); - gMemVtable.free(mem); - } -} - - -static void * -_aligned_alloc_as_malloc(size_t alignment, size_t num_bytes) -{ - BSON_UNUSED(alignment); - - return gMemVtable.malloc(num_bytes); -} - - -/* - *-------------------------------------------------------------------------- - * - * bson_mem_set_vtable -- - * - * This function will change our allocation vtable. - * - * It is imperative that this is called at the beginning of the - * process before any memory has been allocated by the default - * allocator. - * - * Returns: - * None. - * - * Side effects: - * None. - * - *-------------------------------------------------------------------------- - */ - -void -bson_mem_set_vtable(const bson_mem_vtable_t *vtable) -{ - BSON_ASSERT(vtable); - - if (!vtable->malloc || !vtable->calloc || !vtable->realloc || !vtable->free) { - fprintf(stderr, - "Failure to install BSON vtable, " - "missing functions.\n"); - return; - } - - 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 = malloc, - .calloc = calloc, - .realloc = realloc, - .free = free, - .aligned_alloc = _aligned_alloc_impl, - .padding = {0}}; - - bson_mem_set_vtable(&vtable); -} diff --git a/bsonjs/bson/memory.h b/bsonjs/bson/memory.h deleted file mode 100644 index 1c8fc5f..0000000 --- a/bsonjs/bson/memory.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BSON_MEMORY_H_INCLUDED -#define BSON_MEMORY_H_INCLUDED - -#include - - -BSON_BEGIN_DECLS - - -typedef void *(BSON_CALL *bson_realloc_func)(void *mem, size_t num_bytes, void *ctx); - -typedef struct _bson_mem_vtable_t { - void *(BSON_CALL *malloc)(size_t num_bytes); - void *(BSON_CALL *calloc)(size_t n_members, size_t num_bytes); - void *(BSON_CALL *realloc)(void *mem, size_t num_bytes); - void(BSON_CALL *free)(void *mem); - void *(BSON_CALL *aligned_alloc)(size_t alignment, size_t num_bytes); - void *padding[3]; -} bson_mem_vtable_t; - - -BSON_EXPORT(void) -bson_mem_set_vtable(const bson_mem_vtable_t *vtable); -BSON_EXPORT(void) -bson_mem_restore_vtable(void); -BSON_EXPORT(void *) -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_array_alloc(size_t num_elems, size_t elem_size); -BSON_EXPORT(void *) -bson_array_alloc0(size_t num_elems, size_t elem_size); -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); -BSON_EXPORT(void) -bson_free(void *mem); -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)))) -#define BSON_ARRAY_ALLOC(N, T) ((T *)(bson_array_alloc(N, sizeof(T)))) -#define BSON_ARRAY_ALLOC0(N, T) ((T *)(bson_array_alloc0(N, sizeof(T)))) - -BSON_END_DECLS - -#endif // BSON_MEMORY_H_INCLUDED diff --git a/bsonjs/bson/validate-private.h b/bsonjs/bson/validate-private.h deleted file mode 100644 index 440cfd8..0000000 --- a/bsonjs/bson/validate-private.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef BSON_VALIDATE_PRIVATE_H_INCLUDED -#define BSON_VALIDATE_PRIVATE_H_INCLUDED - -#include - -enum { - /** - * @brief This compile-time constant represents the maximum document nesting - * depth permitted by the `bson_validate` family of functions. If the nesting - * depth exceeds this limit, the data will be rejected. - * - * This limit is intentionally larger than the default limit of MongoDB - * server, since we cannot anticipate what a libbson user might actually want - * to do with BSON, and to prevent accidentally rejecting data that the - * server might accept. The main purpose of this limit is to prevent stack - * overflow, not to reject invalid data. - */ - BSON_VALIDATION_MAX_NESTING_DEPTH = 500, -}; - -/** - * @brief Private function backing the implementation of validation. - * - * Validation was previously defined in the overburdened `bson-iter.c`, but it - * is now defined in its own file. - * - * @param bson The document to validate. Must be non-null. - * @param flags Validation control flags - * @param offset Receives the offset at which validation failed. Must be non-null. - * @param error Receives the error describing why validation failed. Must be non-null. - * @return true If the given document has no validation errors - * @return false Otherwise - */ -bool -_bson_validate_impl_v2(const bson_t *bson, bson_validate_flags_t flags, size_t *offset, bson_error_t *error); - -#endif // BSON_VALIDATE_PRIVATE_H_INCLUDED diff --git a/bsonjs/bson/validate.c b/bsonjs/bson/validate.c deleted file mode 100644 index 591d591..0000000 --- a/bsonjs/bson/validate.c +++ /dev/null @@ -1,568 +0,0 @@ -/** - * @file bson/validate.c - * @brief Implementation of BSON document validation - * @date 2025-05-28 - * - * This file implements the backend for the `bson_validate` family of functions. - * - * The `_validate_...` functions all accept `validator* self` as their first parameter, - * and must `return false` AND set `self->error` if-and-only-if they encounter a validation error. - * If a function returns true, it is assumed that validation of that item succeeded. - * - * For brevity, the `require...` macros are defined, which check conditions, set errors, - * and `return false` inline. - * - * @copyright Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include - -#include -#include - -#include -#include - -/** - * @brief User parameters for validation behavior. These correspond to the various - * flags that can be given when the user requests validation - */ -typedef struct { - /** - * @brief Should we allow invalid UTF-8 in string components? - * - * This affects the behavior of validation of key strings and string-like - * elements that require UTF-8 encoding. - * - * Technically invalid UTF-8 is invalid in BSON, but applications may already - * rely on this being accepted. - */ - bool allow_invalid_utf8; - /** - * @brief Should we allow a zero-valued codepoint in text? - * - * Unicode U+0000 is a valid codepoint, but a lot of software doesn't like - * it and handles it poorly. By default, we reject it, but the user may - * want to allow it. - * - * Note that because element keys rely on null termination, element keys - * cannot contain U+0000 by construction. - */ - bool allow_null_in_utf8; - /// Should we allow element key strings to be empty strings? - bool allow_empty_keys; - /// Should we allow ASCII dot "." in element key strings? - bool allow_dot_in_keys; - /** - * @brief Check for special element keys that begin with an ASCII dollar "$" - * - * By default, we ignore them and treat them as regular elements. If this is - * enabled, we reject key strings that start with a dollar, unless it is a - * special extended JSON DBRef document. - * - * This also enables DBRef validation, which checks the structure of a document - * whose first key is "$ref". - */ - bool check_special_dollar_keys; -} validation_params; - -/** - * @brief State for a validator. - */ -typedef struct { - /// The parameters that control validation behavior - const validation_params *params; - /// Error storage that is updated if any validation encounters an error - bson_error_t error; - /// The zero-based index of the byte where validation stopped in case of an error. - size_t error_offset; -} validator; - -// Undef these macros, if they are defined. -#ifdef require_with_error -#undef require_with_error -#endif -#ifdef require -#undef require -#endif -#ifdef require_advance -#undef require_advance -#endif - -/** - * @brief Check that the given condition is satisfied, or set an error and return `false` - * - * @param Condition The condition that should evaluate to `true` - * @param Offset The byte offset where an error should be indicated. - * @param Code The error code that should be set if the condition fails - * @param ... The error string and format arguments to be used in the error message - * - * This macro assumes a `validator* self` is in scope. This macro will evaluate `return false` - * if the given condition is not true. - */ -#define require_with_error(Condition, Offset, Code, ...) \ - if (!(Condition)) { \ - self->error_offset = (Offset); \ - bson_set_error(&self->error, BSON_ERROR_INVALID, Code, __VA_ARGS__); \ - return false; \ - } else \ - ((void)0) - -/** - * @brief Check that the given condition is satisfied, or `return false` immediately. - * - * This macro does not modify the validator state. It only does an early-return. - */ -#define require(Cond) \ - if (!(Cond)) { \ - return false; \ - } else \ - ((void)0) - -/** - * @brief Advance the pointed-to iterator, check for errors, and test whether we are done. - * - * @param DoneVar An l-value of type `bool` that is set to `true` if the iterator hit the end of - * the document, otherwise `false` - * @param IteratorPointer An expression of type `bson_iter_t*`, which will be advanced. - * - * If advancing the iterator results in a decoding error, then this macro sets an error - * on the `validator* self` that is in scope and will immediately `return false`. - */ -#define require_advance(DoneVar, IteratorPointer) \ - if ((DoneVar = !bson_iter_next(IteratorPointer))) { \ - /* The iterator indicates that it stopped */ \ - if ((IteratorPointer)->err_off) { \ - /* The iterator stopped because of a decoding error */ \ - require_with_error(false, (IteratorPointer)->err_off, BSON_VALIDATE_CORRUPT, "corrupt BSON"); \ - } \ - } else \ - ((void)0) - -// Test if the element's key is equal to the given string -static bool -_key_is(bson_iter_t const *iter, const char *const key) -{ - BSON_ASSERT_PARAM(iter); - BSON_ASSERT_PARAM(key); - return !strcmp(bson_iter_key(iter), key); -} - -/** - * @brief Validate a document or array object, recursively. - * - * @param self The validator which will be updated and used to do the validation - * @param bson The object to be validated - * @param depth The validation depth. We indicate an error if this exceeds a limit. - * @return true If the object is valid - * @return false Otherwise - */ -static bool -_validate_doc(validator *self, const bson_t *bson, int depth); - -/** - * @brief Validate a UTF-8 string, if-and-only-if UTF-8 validation is requested - * - * @param self Pointer to the validator object - * @param offset The byte-offset of the string, used to set the error offset - * @param u8 Pointer to the first byte in a UTF-8 string - * @param u8len The length of the array pointed-to by `u8` - * @return true If the UTF-8 string is valid, or if UTF-8 validation is disabled - * @return false If UTF-8 validation is requested, AND (the UTF-8 string is invalid OR (UTF-8 strings should not contain - * null characters and the UTF-8 string contains a null character)) - */ -static bool -_maybe_validate_utf8(validator *self, size_t offset, const char *u8, size_t u8len) -{ - BSON_ASSERT_PARAM(self); - BSON_ASSERT_PARAM(u8); - if (self->params->allow_invalid_utf8) { - // We are not doing UTF-8 checks, so always succeed - return true; - } - // Validate UTF-8 - const bool u8okay = bson_utf8_validate(u8, u8len, self->params->allow_null_in_utf8); - if (u8okay) { - // Valid UTF-8, no more checks - return true; - } - // Validation error. It may be invalid UTF-8, or it could be valid UTF-8 with a disallowed null - if (!self->params->allow_null_in_utf8) { - // We are disallowing null in UTF-8. Check whether it is invalid UTF-8, or is - // valid UTF-8 with a null character - const bool u8okay_with_null = bson_utf8_validate(u8, u8len, true); - if (u8okay_with_null) { - // The UTF-8 is valid, but contains a null character. - require_with_error( - false, offset, BSON_VALIDATE_UTF8_ALLOW_NULL, "UTF-8 string contains a U+0000 (null) character"); - } - } - // The UTF-8 is invalid, regardless of whether it contains a null character - require_with_error(false, offset, BSON_VALIDATE_UTF8, "Text element is not valid UTF-8"); -} - -// Same as `_maybe_validate_u8`, but relies on a null-terminated C string to get the string length -static bool -_maybe_validate_utf8_cstring(validator *self, size_t offset, const char *const u8) -{ - BSON_ASSERT_PARAM(self); - BSON_ASSERT_PARAM(u8); - return _maybe_validate_utf8(self, offset, u8, strlen(u8)); -} - -/** - * @brief Validate a string-like element (UTF-8, Symbol, or Code) - * - * This function relies on the representation of the text-like elements within - * the iterator struct to reduce code dup around text validation. - */ -static bool -_validate_stringlike_element(validator *self, bson_iter_t const *iter) -{ - BSON_ASSERT_PARAM(self); - BSON_ASSERT_PARAM(iter); - // iter->d1 is the offset to the string header. Subtract 1 to exclude the null terminator - const uint32_t u8len = mlib_read_u32le(iter->raw + iter->d1) - 1; - // iter->d2 is the offset to the first byte of the string - const char *u8 = (const char *)iter->raw + iter->d2; - return _maybe_validate_utf8(self, iter->off, u8, u8len); -} - -static bool -_validate_regex_elem(validator *self, bson_iter_t const *iter) -{ - BSON_ASSERT_PARAM(self); - BSON_ASSERT_PARAM(iter); - mlib_check(BSON_ITER_HOLDS_REGEX(iter)); - const char *opts; - const char *const rx = bson_iter_regex(iter, &opts); - mlib_check(rx); - mlib_check(opts); - return _maybe_validate_utf8_cstring(self, iter->off, rx) // - && _maybe_validate_utf8_cstring(self, iter->off, opts); -} - -static bool -_validate_codewscope_elem(validator *self, bson_iter_t const *iter, int depth) -{ - BSON_ASSERT_PARAM(self); - BSON_ASSERT_PARAM(iter); - mlib_check(BSON_ITER_HOLDS_CODEWSCOPE(iter)); - // Extract the code and the scope object - uint8_t const *doc; - uint32_t doc_len; - uint32_t u8len; - const char *const u8 = bson_iter_codewscope(iter, &u8len, &doc_len, &doc); - bson_t scope; - require_with_error( - bson_init_static(&scope, doc, doc_len), iter->off, BSON_VALIDATE_CORRUPT, "corrupt scope document"); - - // Validate the code string - require(_maybe_validate_utf8(self, iter->off, u8, u8len)); - - // Now we validate the scope object. - // Don't validate the scope document using the parent parameters, because it should - // be treated as an opaque closure of JS variables. - validation_params const scope_params = { - // JS object keys can contain dots - .allow_dot_in_keys = true, - // JS object keys can be empty - .allow_empty_keys = true, - // JS strings can contain null bytes - .allow_null_in_utf8 = true, - // JS strings need to encode properly - .allow_invalid_utf8 = false, - // JS allows object keys to have dollars - .check_special_dollar_keys = false, - }; - validator scope_validator = {.params = &scope_params}; - // We could do more validation that the scope keys are valid JS identifiers, - // but that would require using a full Unicode database. - if (_validate_doc(&scope_validator, &scope, depth)) { - // No error - return true; - } - // Validation error. Copy the error message, adding the name of the bad element - bson_set_error(&self->error, - scope_validator.error.domain, - scope_validator.error.code, - "Error in scope document for element \"%s\": %s", - bson_iter_key(iter), - scope_validator.error.message); - // Adjust the error offset by the offset of the iterator - self->error_offset = scope_validator.error_offset + iter->off; - return false; -} - -// Validate an element's key string according to the validation rules -static bool -_validate_element_key(validator *self, bson_iter_t const *iter) -{ - BSON_ASSERT_PARAM(self); - BSON_ASSERT_PARAM(iter); - - const char *const key = bson_iter_key(iter); - mlib_check(key); - const size_t key_len = bson_iter_key_len(iter); - - // Check the UTF-8 of the key - require(_maybe_validate_utf8(self, iter->off, key, key_len)); - - // Check for special keys - if (self->params->check_special_dollar_keys) { - // dollar-keys are checked during the startup of _validate_doc. If we get here, there's a problem. - require_with_error( - key[0] != '$', iter->off, BSON_VALIDATE_DOLLAR_KEYS, "Disallowed '$' in element key: \"%s\"", key); - } - - if (!self->params->allow_empty_keys) { - require_with_error(key_len != 0, iter->off, BSON_VALIDATE_EMPTY_KEYS, "Element key cannot be an empty string"); - } - - if (!self->params->allow_dot_in_keys) { - require_with_error( - !strstr(key, "."), iter->off, BSON_VALIDATE_DOT_KEYS, "Disallowed '.' in element key: \"%s\"", key); - } - - return true; -} - -// Extract a document referred-to by the given iterator. It must point to a -// document or array element. Returns `false` if `bson_init_static` returns false -static bool -_get_subdocument(bson_t *subdoc, bson_iter_t const *iter) -{ - BSON_ASSERT_PARAM(subdoc); - BSON_ASSERT_PARAM(iter); - uint32_t len = mlib_read_u32le(iter->raw + iter->d1); - uint8_t const *data = (uint8_t const *)iter->raw + iter->d1; - return bson_init_static(subdoc, data, len); -} - -// Validate the value of an element, without checking its key -static bool -_validate_element_value(validator *self, bson_iter_t const *iter, int depth) -{ - BSON_ASSERT_PARAM(self); - BSON_ASSERT_PARAM(iter); - - const bson_type_t type = bson_iter_type(iter); - switch (type) { - default: - case BSON_TYPE_EOD: - BSON_UNREACHABLE("Validation execution encountered an element of type 0x0, but this should not happen as tag " - "validation is handled before we get to this point."); - case BSON_TYPE_DOUBLE: - case BSON_TYPE_NULL: - case BSON_TYPE_OID: - case BSON_TYPE_INT32: - case BSON_TYPE_INT64: - case BSON_TYPE_MINKEY: - case BSON_TYPE_MAXKEY: - case BSON_TYPE_TIMESTAMP: - case BSON_TYPE_UNDEFINED: - case BSON_TYPE_DECIMAL128: - case BSON_TYPE_DATE_TIME: - case BSON_TYPE_BOOL: - // No validation on these simple scalar elements. `bson_iter_next` does validation - // on these objects for us. - return true; - case BSON_TYPE_BINARY: - // Note: BSON binary validation is handled by bson_iter_next, which checks the - // internal structure properly. If we get here, then the binary data is okay. - return true; - case BSON_TYPE_DBPOINTER: - // DBPointer contains more than just a string, but we only need to validate - // the string component, which happens to align with the repr of other stringlike - // elements. bson_iter_next will do the validation on the element's size. - //! fallthrough - case BSON_TYPE_SYMBOL: - case BSON_TYPE_CODE: - case BSON_TYPE_UTF8: - return _validate_stringlike_element(self, iter); - case BSON_TYPE_DOCUMENT: - case BSON_TYPE_ARRAY: { - bson_t doc; - require_with_error(_get_subdocument(&doc, iter), iter->off, BSON_VALIDATE_CORRUPT, "corrupt BSON"); - if (_validate_doc(self, &doc, depth)) { - // No error - return true; - } - // Error in subdocument. Adjust the error offset for the current iterator position, - // plus the key length, plus 2 for the tag and key's null terminator. - self->error_offset += iter->off + bson_iter_key_len(iter) + 2; - return false; - } - - case BSON_TYPE_REGEX: - return _validate_regex_elem(self, iter); - case BSON_TYPE_CODEWSCOPE: - return _validate_codewscope_elem(self, iter, depth); - } -} - -// Validate a single BSON element referred-to by the given iterator -static bool -_validate_element(validator *self, bson_iter_t *iter, int depth) -{ - BSON_ASSERT_PARAM(self); - BSON_ASSERT_PARAM(iter); - return _validate_element_key(self, iter) && _validate_element_value(self, iter, depth); -} - -/** - * @brief Validate the elements of a document, beginning with the element pointed-to - * by the given iterator. - */ -static bool -_validate_remaining_elements(validator *self, bson_iter_t *iter, int depth) -{ - BSON_ASSERT_PARAM(self); - BSON_ASSERT_PARAM(iter); - bool done = false; - while (!done) { - require(_validate_element(self, iter, depth)); - require_advance(done, iter); - } - return true; -} - -// Do validation for a DBRef document, indicated by a leading $ref key -static bool -_validate_dbref(validator *self, bson_iter_t *iter, int depth) -{ - BSON_ASSERT_PARAM(self); - BSON_ASSERT_PARAM(iter); - - // The iterator must be pointing to the initial $ref element - mlib_check(_key_is(iter, "$ref")); - // Check that $ref is a UTF-8 element - require_with_error( - BSON_ITER_HOLDS_UTF8(iter), iter->off, BSON_VALIDATE_DOLLAR_KEYS, "$ref element must be a UTF-8 element"); - require(_validate_element_value(self, iter, depth)); - - // We require an $id as the next element - bool done; - require_advance(done, iter); - require_with_error( - !done && _key_is(iter, "$id"), iter->off, BSON_VALIDATE_DOLLAR_KEYS, "Expected an $id element following $ref"); - // While $id is typically a OID value, it is not constraint to any specific type, so - // we just validate it as an arbitrary value. - require(_validate_element_value(self, iter, depth)); - - // We should stop, or we should have a $db, or we may have other elements - require_advance(done, iter); - if (done) { - // No more elements. Nothing left to check - return true; - } - // If it's a $db, check that it's a UTF-8 string - if (_key_is(iter, "$db")) { - require_with_error(BSON_ITER_HOLDS_UTF8(iter), - iter->off, - BSON_VALIDATE_DOLLAR_KEYS, - "$db element in DBRef must be a UTF-8 element"); - require(_validate_element_value(self, iter, depth)); - // Advance past the $db - require_advance(done, iter); - if (done) { - // Nothing left to do - return true; - } - } - // All subsequent elements should be validated as normal, and we don't expect - // any more $-keys - return _validate_remaining_elements(self, iter, depth); -} - -// If we are validating special $-keys, validate a document whose first element is a $-key -static bool -_validate_dollar_doc(validator *self, bson_iter_t *iter, int depth) -{ - BSON_ASSERT_PARAM(self); - BSON_ASSERT_PARAM(iter); - if (_key_is(iter, "$ref")) { - return _validate_dbref(self, iter, depth); - } - // Have the element key validator issue an error message about the bad $-key - bool okay = _validate_element_key(self, iter); - mlib_check(!okay); - return false; -} - -static bool -_validate_doc(validator *self, const bson_t *bson, int depth) -{ - BSON_ASSERT_PARAM(self); - BSON_ASSERT_PARAM(bson); - - require_with_error( - depth <= BSON_VALIDATION_MAX_NESTING_DEPTH, 0, BSON_VALIDATE_CORRUPT, "BSON document nesting depth is too deep"); - // We increment the depth here, otherwise we'd have `depth + 1` in several places. - ++depth; - - // Initialize an iterator into the document to be validated - bson_iter_t iter; - require_with_error( - bson_iter_init(&iter, bson), 0, BSON_VALIDATE_CORRUPT, "Document header corruption, unable to iterate"); - bool done; - require_advance(done, &iter); - if (done) { - // Nothing to check (empty doc/array) - return true; - } - - // Check if the first key starts with a dollar - if (self->params->check_special_dollar_keys) { - const char *const key = bson_iter_key(&iter); - if (key[0] == '$') { - return _validate_dollar_doc(self, &iter, depth); - } - } - - return _validate_remaining_elements(self, &iter, depth); -} - -// This private function is called by `bson_validate_with_error_and_offset` -bool -_bson_validate_impl_v2(const bson_t *bson, bson_validate_flags_t flags, size_t *offset, bson_error_t *error) -{ - BSON_ASSERT_PARAM(bson); - BSON_ASSERT_PARAM(offset); - BSON_ASSERT_PARAM(error); - - // Clear the error - *error = (bson_error_t){0}; - - // Initialize validation parameters - validation_params const params = { - .allow_invalid_utf8 = !(flags & BSON_VALIDATE_UTF8), - .allow_null_in_utf8 = flags & BSON_VALIDATE_UTF8_ALLOW_NULL, - .check_special_dollar_keys = (flags & BSON_VALIDATE_DOLLAR_KEYS), - .allow_dot_in_keys = !(flags & BSON_VALIDATE_DOT_KEYS), - .allow_empty_keys = !(flags & BSON_VALIDATE_EMPTY_KEYS), - }; - - // Start the validator on the root document - validator v = {.params = ¶ms}; - bool okay = _validate_doc(&v, bson, 0); - *offset = v.error_offset; - *error = v.error; - mlib_check(okay == (v.error.code == 0) && - "Validation routine should return `false` if-and-only-if it sets an error code"); - return okay; -} diff --git a/bsonjs/bson/version.h b/bsonjs/bson/version.h deleted file mode 100644 index 6edacb7..0000000 --- a/bsonjs/bson/version.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// clang-format off - -#ifndef BSON_VERSION_H -#define BSON_VERSION_H - - -/** - * BSON_MAJOR_VERSION: - * - * BSON major version component (e.g. 1 if %BSON_VERSION is 1.2.3) - */ -#define BSON_MAJOR_VERSION (2) - - -/** - * BSON_MINOR_VERSION: - * - * BSON minor version component (e.g. 2 if %BSON_VERSION is 1.2.3) - */ -#define BSON_MINOR_VERSION (5) - - -/** - * BSON_MICRO_VERSION: - * - * BSON micro version component (e.g. 3 if %BSON_VERSION is 1.2.3) - */ -#define BSON_MICRO_VERSION (0) - - -/** - * BSON_PRERELEASE_VERSION: - * - * BSON prerelease version component (e.g. pre if %BSON_VERSION is 1.2.3-pre) - */ -#define BSON_PRERELEASE_VERSION () - -/** - * BSON_VERSION: - * - * BSON version. - */ -#define BSON_VERSION (2.5.0) - - -/** - * BSON_VERSION_S: - * - * BSON version, encoded as a string, useful for printing and - * concatenation. - */ -#define BSON_VERSION_S "2.5.0" - - -/** - * BSON_VERSION_HEX: - * - * BSON version, encoded as an hexadecimal number, useful for - * integer comparisons. - */ -#define BSON_VERSION_HEX (BSON_MAJOR_VERSION << 24 | \ - BSON_MINOR_VERSION << 16 | \ - BSON_MICRO_VERSION << 8) - - -/** - * BSON_CHECK_VERSION: - * @major: required major version - * @minor: required minor version - * @micro: required micro version - * - * Compile-time version checking. Evaluates to %TRUE if the version - * of BSON is greater than or equal to the required one. - */ -#define BSON_CHECK_VERSION(major,minor,micro) \ - (BSON_MAJOR_VERSION > (major) || \ - (BSON_MAJOR_VERSION == (major) && BSON_MINOR_VERSION > (minor)) || \ - (BSON_MAJOR_VERSION == (major) && BSON_MINOR_VERSION == (minor) && \ - BSON_MICRO_VERSION >= (micro))) - -#endif /* BSON_VERSION_H */ diff --git a/bsonjs/common/common-atomic-private.h b/bsonjs/common/common-atomic-private.h deleted file mode 100644 index 9623a06..0000000 --- a/bsonjs/common/common-atomic-private.h +++ /dev/null @@ -1,657 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - - -#ifndef MONGO_C_DRIVER_COMMON_ATOMIC_PRIVATE_H -#define MONGO_C_DRIVER_COMMON_ATOMIC_PRIVATE_H - - -#include // BSON_INLINE - -#ifdef _MSC_VER -#include -#endif - - -enum mcommon_memory_order { - mcommon_memory_order_seq_cst, - mcommon_memory_order_acquire, - mcommon_memory_order_release, - mcommon_memory_order_relaxed, - mcommon_memory_order_acq_rel, - mcommon_memory_order_consume, -}; - -#if defined(_M_ARM) /* MSVC memorder atomics are only avail on ARM */ -#define MSVC_MEMORDER_SUFFIX(X) X -#else -#define MSVC_MEMORDER_SUFFIX(X) -#endif - -#if defined(USE_LEGACY_GCC_ATOMICS) || (!defined(__clang__) && __GNUC__ == 4) || defined(__xlC__) -#define MCOMMON_USE_LEGACY_GCC_ATOMICS -#else -#undef MCOMMON_USE_LEGACY_GCC_ATOMICS -#endif - -/* Not all GCC-like compilers support the current __atomic built-ins. Older - * GCC (pre-5) used different built-ins named with the __sync prefix. When - * compiling with such older GCC versions, it is necessary to use the applicable - * functions, which requires redefining BSON_IF_GNU_LIKE and defining the - * additional MCOMMON_IF_GNU_LEGACY_ATOMICS macro here. */ -#ifdef MCOMMON_USE_LEGACY_GCC_ATOMICS -#undef BSON_IF_GNU_LIKE -#define BSON_IF_GNU_LIKE(...) -#define MCOMMON_IF_GNU_LEGACY_ATOMICS(...) __VA_ARGS__ -#else -#define MCOMMON_IF_GNU_LEGACY_ATOMICS(...) -#endif - -/* CDRIVER-4229 zSeries with gcc 4.8.4 produces illegal instructions for int and - * int32 atomic intrinsics. */ -#if defined(__s390__) || defined(__s390x__) || defined(__zarch__) -#define MCOMMON_EMULATE_INT32 -#define MCOMMON_EMULATE_INT -#endif - -/* 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 MCOMMON_EMULATE_PTR -#endif - -#define DEF_ATOMIC_OP(MSVC_Intrinsic, GNU_Intrinsic, GNU_Legacy_Intrinsic, Order, ...) \ - do { \ - switch (Order) { \ - case mcommon_memory_order_acq_rel: \ - BSON_IF_MSVC(return MSVC_Intrinsic(__VA_ARGS__);) \ - BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_ACQ_REL);) \ - MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);) \ - case mcommon_memory_order_seq_cst: \ - BSON_IF_MSVC(return MSVC_Intrinsic(__VA_ARGS__);) \ - BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_SEQ_CST);) \ - MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);) \ - case mcommon_memory_order_acquire: \ - BSON_IF_MSVC(return BSON_CONCAT(MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX(_acq))(__VA_ARGS__);) \ - BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_ACQUIRE);) \ - MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);) \ - case mcommon_memory_order_consume: \ - BSON_IF_MSVC(return BSON_CONCAT(MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX(_acq))(__VA_ARGS__);) \ - BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_CONSUME);) \ - MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);) \ - case mcommon_memory_order_release: \ - BSON_IF_MSVC(return BSON_CONCAT(MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX(_rel))(__VA_ARGS__);) \ - BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_RELEASE);) \ - MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);) \ - case mcommon_memory_order_relaxed: \ - BSON_IF_MSVC(return BSON_CONCAT(MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX(_nf))(__VA_ARGS__);) \ - BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_RELAXED);) \ - MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);) \ - default: \ - BSON_UNREACHABLE("Invalid mcommon_memory_order value"); \ - } \ - } 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);) \ - MCOMMON_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);) \ - MCOMMON_IF_GNU_LEGACY_ATOMICS(__typeof__(ExpectActualVar) _val; \ - _val = __sync_val_compare_and_swap(Ptr, ExpectActualVar, NewValue); \ - ExpectActualVar = _val;) \ - } while (0) - - -// on Windows, when invoking the appropriate intrinsic, cast to the type the intrinsic is declared with to avoid -// warnings irrespective of the signedness of Type; all these operations are bit-level and don't care about the sign -#define DECL_ATOMIC_INTEGRAL(NamePart, Type, VCIntrinType, VCIntrinSuffix) \ - static BSON_INLINE Type mcommon_atomic_##NamePart##_fetch_add( \ - Type volatile *a, Type addend, enum mcommon_memory_order ord) \ - { \ - DEF_ATOMIC_OP(BSON_CONCAT(_InterlockedExchangeAdd, VCIntrinSuffix), \ - __atomic_fetch_add, \ - __sync_fetch_and_add, \ - ord, \ - BSON_IF_MSVC((volatile VCIntrinType *)) a, \ - addend); \ - } \ - \ - static BSON_INLINE Type mcommon_atomic_##NamePart##_fetch_sub( \ - Type volatile *a, Type subtrahend, enum mcommon_memory_order ord) \ - { \ - /* MSVC doesn't have a subtract intrinsic, so just reuse addition */ \ - BSON_IF_MSVC(return mcommon_atomic_##NamePart##_fetch_add(a, -subtrahend, ord);) \ - BSON_IF_GNU_LIKE(DEF_ATOMIC_OP(~, __atomic_fetch_sub, ~, ord, a, subtrahend);) \ - MCOMMON_IF_GNU_LEGACY_ATOMICS(DEF_ATOMIC_OP(~, ~, __sync_fetch_and_sub, ord, a, subtrahend);) \ - } \ - \ - static BSON_INLINE Type mcommon_atomic_##NamePart##_fetch(Type volatile const *a, enum mcommon_memory_order order) \ - { \ - /* MSVC doesn't have a load intrinsic, so just add zero */ \ - BSON_IF_MSVC(return mcommon_atomic_##NamePart##_fetch_add((Type volatile *)a, 0, order);) \ - /* GNU doesn't want RELEASE order for the fetch operation, so we can't \ - * just use DEF_ATOMIC_OP. */ \ - BSON_IF_GNU_LIKE(switch (order) { \ - case mcommon_memory_order_release: /* Fall back to seqcst */ \ - case mcommon_memory_order_acq_rel: /* Fall back to seqcst */ \ - case mcommon_memory_order_seq_cst: \ - return __atomic_load_n(a, __ATOMIC_SEQ_CST); \ - case mcommon_memory_order_acquire: \ - return __atomic_load_n(a, __ATOMIC_ACQUIRE); \ - case mcommon_memory_order_consume: \ - return __atomic_load_n(a, __ATOMIC_CONSUME); \ - case mcommon_memory_order_relaxed: \ - return __atomic_load_n(a, __ATOMIC_RELAXED); \ - default: \ - BSON_UNREACHABLE("Invalid mcommon_memory_order value"); \ - }) \ - MCOMMON_IF_GNU_LEGACY_ATOMICS({ \ - BSON_UNUSED(order); \ - __sync_synchronize(); \ - return *a; \ - }) \ - } \ - \ - static BSON_INLINE Type mcommon_atomic_##NamePart##_exchange( \ - Type volatile *a, Type value, enum mcommon_memory_order ord) \ - { \ - BSON_IF_MSVC(DEF_ATOMIC_OP(BSON_CONCAT(_InterlockedExchange, VCIntrinSuffix), \ - ~, \ - ~, \ - ord, \ - BSON_IF_MSVC((volatile VCIntrinType *)) a, \ - value);) \ - /* GNU doesn't want CONSUME order for the exchange operation, so we \ - * cannot use DEF_ATOMIC_OP. */ \ - BSON_IF_GNU_LIKE(switch (ord) { \ - case mcommon_memory_order_acq_rel: \ - return __atomic_exchange_n(a, value, __ATOMIC_ACQ_REL); \ - case mcommon_memory_order_release: \ - return __atomic_exchange_n(a, value, __ATOMIC_RELEASE); \ - case mcommon_memory_order_seq_cst: \ - return __atomic_exchange_n(a, value, __ATOMIC_SEQ_CST); \ - case mcommon_memory_order_consume: /* Fall back to acquire */ \ - case mcommon_memory_order_acquire: \ - return __atomic_exchange_n(a, value, __ATOMIC_ACQUIRE); \ - case mcommon_memory_order_relaxed: \ - return __atomic_exchange_n(a, value, __ATOMIC_RELAXED); \ - default: \ - BSON_UNREACHABLE("Invalid mcommon_memory_order value"); \ - }) \ - MCOMMON_IF_GNU_LEGACY_ATOMICS(BSON_UNUSED(ord); return __sync_val_compare_and_swap(a, *a, value);) \ - } \ - \ - static BSON_INLINE Type mcommon_atomic_##NamePart##_compare_exchange_strong( \ - Type volatile *a, Type expect, Type new_value, enum mcommon_memory_order ord) \ - { \ - Type actual = expect; \ - switch (ord) { \ - case mcommon_memory_order_release: \ - case mcommon_memory_order_acq_rel: \ - case mcommon_memory_order_seq_cst: \ - DEF_ATOMIC_CMPEXCH_STRONG( \ - VCIntrinSuffix, , __ATOMIC_SEQ_CST, BSON_IF_MSVC((volatile VCIntrinType *)) a, actual, new_value); \ - break; \ - case mcommon_memory_order_acquire: \ - DEF_ATOMIC_CMPEXCH_STRONG(VCIntrinSuffix, \ - MSVC_MEMORDER_SUFFIX(_acq), \ - __ATOMIC_ACQUIRE, \ - BSON_IF_MSVC((volatile VCIntrinType *)) a, \ - actual, \ - new_value); \ - break; \ - case mcommon_memory_order_consume: \ - DEF_ATOMIC_CMPEXCH_STRONG(VCIntrinSuffix, \ - MSVC_MEMORDER_SUFFIX(_acq), \ - __ATOMIC_CONSUME, \ - BSON_IF_MSVC((volatile VCIntrinType *)) a, \ - actual, \ - new_value); \ - break; \ - case mcommon_memory_order_relaxed: \ - DEF_ATOMIC_CMPEXCH_STRONG(VCIntrinSuffix, \ - MSVC_MEMORDER_SUFFIX(_nf), \ - __ATOMIC_RELAXED, \ - BSON_IF_MSVC((volatile VCIntrinType *)) a, \ - actual, \ - new_value); \ - break; \ - default: \ - BSON_UNREACHABLE("Invalid mcommon_memory_order value"); \ - } \ - return actual; \ - } \ - \ - static BSON_INLINE Type mcommon_atomic_##NamePart##_compare_exchange_weak( \ - Type volatile *a, Type expect, Type new_value, enum mcommon_memory_order ord) \ - { \ - Type actual = expect; \ - switch (ord) { \ - case mcommon_memory_order_release: \ - case mcommon_memory_order_acq_rel: \ - case mcommon_memory_order_seq_cst: \ - DEF_ATOMIC_CMPEXCH_WEAK( \ - VCIntrinSuffix, , __ATOMIC_SEQ_CST, BSON_IF_MSVC((volatile VCIntrinType *)) a, actual, new_value); \ - break; \ - case mcommon_memory_order_acquire: \ - DEF_ATOMIC_CMPEXCH_WEAK(VCIntrinSuffix, \ - MSVC_MEMORDER_SUFFIX(_acq), \ - __ATOMIC_ACQUIRE, \ - BSON_IF_MSVC((volatile VCIntrinType *)) a, \ - actual, \ - new_value); \ - break; \ - case mcommon_memory_order_consume: \ - DEF_ATOMIC_CMPEXCH_WEAK(VCIntrinSuffix, \ - MSVC_MEMORDER_SUFFIX(_acq), \ - __ATOMIC_CONSUME, \ - BSON_IF_MSVC((volatile VCIntrinType *)) a, \ - actual, \ - new_value); \ - break; \ - case mcommon_memory_order_relaxed: \ - DEF_ATOMIC_CMPEXCH_WEAK(VCIntrinSuffix, \ - MSVC_MEMORDER_SUFFIX(_nf), \ - __ATOMIC_RELAXED, \ - BSON_IF_MSVC((volatile VCIntrinType *)) a, \ - actual, \ - new_value); \ - break; \ - default: \ - BSON_UNREACHABLE("Invalid mcommon_memory_order value"); \ - } \ - return actual; \ - } - -#define DECL_ATOMIC_STDINT(Name, VCIntrinType, VCSuffix) DECL_ATOMIC_INTEGRAL(Name, Name##_t, VCIntrinType, VCSuffix) - -#if defined(_MSC_VER) || defined(MCOMMON_USE_LEGACY_GCC_ATOMICS) -/* MSVC and GCC require built-in types (not typedefs) for their atomic - * intrinsics. - * When using clang-cl it pretends to be MSVC (by defining _MSC_VER) - * but then complains about int8_t actual arguments (signed char on Windows) vs char formal arguments mismatch - */ -#if defined(_MSC_VER) && !defined(__clang__) -#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, char, 8) -DECL_ATOMIC_INTEGRAL(int16, short, short, 16) -#if !defined(MCOMMON_EMULATE_INT32) -DECL_ATOMIC_INTEGRAL(int32, DECL_ATOMIC_INTEGRAL_INT32, long, ) -#endif -#if !defined(MCOMMON_EMULATE_INT) -DECL_ATOMIC_INTEGRAL(int, DECL_ATOMIC_INTEGRAL_INT, long, ) -#endif -#else -/* Other compilers that we support provide generic intrinsics */ -DECL_ATOMIC_STDINT(int8, char, 8) -DECL_ATOMIC_STDINT(int16, short, 16) -#if !defined(MCOMMON_EMULATE_INT32) -DECL_ATOMIC_STDINT(int32, long, ) -#endif -#if !defined(MCOMMON_EMULATE_INT) -DECL_ATOMIC_INTEGRAL(int, int, long, ) -#endif -#endif - -#ifndef DECL_ATOMIC_INTEGRAL_INT32 -#define DECL_ATOMIC_INTEGRAL_INT32 int32_t -#endif - -#define _mcommon_emul_atomic_int64_fetch_add COMMON_NAME(emul_atomic_int64_fetch_add) -#define _mcommon_emul_atomic_int64_exchange COMMON_NAME(emul_atomic_int64_exchange) -#define _mcommon_emul_atomic_int64_compare_exchange_strong COMMON_NAME(emul_atomic_int64_compare_exchange_strong) -#define _mcommon_emul_atomic_int64_compare_exchange_weak COMMON_NAME(emul_atomic_int64_compare_exchange_weak) -#define _mcommon_emul_atomic_int32_fetch_add COMMON_NAME(emul_atomic_int32_fetch_add) -#define _mcommon_emul_atomic_int32_exchange COMMON_NAME(emul_atomic_int32_exchange) -#define _mcommon_emul_atomic_int32_compare_exchange_strong COMMON_NAME(emul_atomic_int32_compare_exchange_strong) -#define _mcommon_emul_atomic_int32_compare_exchange_weak COMMON_NAME(emul_atomic_int32_compare_exchange_weak) -#define _mcommon_emul_atomic_int_fetch_add COMMON_NAME(emul_atomic_int_fetch_add) -#define _mcommon_emul_atomic_int_exchange COMMON_NAME(emul_atomic_int_exchange) -#define _mcommon_emul_atomic_int_compare_exchange_strong COMMON_NAME(emul_atomic_int_compare_exchange_strong) -#define _mcommon_emul_atomic_int_compare_exchange_weak COMMON_NAME(emul_atomic_int_compare_exchange_weak) -#define _mcommon_emul_atomic_ptr_exchange COMMON_NAME(emul_atomic_ptr_exchange) -#define mcommon_thrd_yield COMMON_NAME(thrd_yield) - -int64_t -_mcommon_emul_atomic_int64_fetch_add(int64_t volatile *val, int64_t v, enum mcommon_memory_order); -int64_t -_mcommon_emul_atomic_int64_exchange(int64_t volatile *val, int64_t v, enum mcommon_memory_order); -int64_t -_mcommon_emul_atomic_int64_compare_exchange_strong(int64_t volatile *val, - int64_t expect_value, - int64_t new_value, - enum mcommon_memory_order); - -int64_t -_mcommon_emul_atomic_int64_compare_exchange_weak(int64_t volatile *val, - int64_t expect_value, - int64_t new_value, - enum mcommon_memory_order); - -int32_t -_mcommon_emul_atomic_int32_fetch_add(int32_t volatile *val, int32_t v, enum mcommon_memory_order); -int32_t -_mcommon_emul_atomic_int32_exchange(int32_t volatile *val, int32_t v, enum mcommon_memory_order); -int32_t -_mcommon_emul_atomic_int32_compare_exchange_strong(int32_t volatile *val, - int32_t expect_value, - int32_t new_value, - enum mcommon_memory_order); - -int32_t -_mcommon_emul_atomic_int32_compare_exchange_weak(int32_t volatile *val, - int32_t expect_value, - int32_t new_value, - enum mcommon_memory_order); - -int -_mcommon_emul_atomic_int_fetch_add(int volatile *val, int v, enum mcommon_memory_order); -int -_mcommon_emul_atomic_int_exchange(int volatile *val, int v, enum mcommon_memory_order); -int -_mcommon_emul_atomic_int_compare_exchange_strong(int volatile *val, - int expect_value, - int new_value, - enum mcommon_memory_order); - -int -_mcommon_emul_atomic_int_compare_exchange_weak(int volatile *val, - int expect_value, - int new_value, - enum mcommon_memory_order); - -void * -_mcommon_emul_atomic_ptr_exchange(void *volatile *val, void *v, enum mcommon_memory_order); - -void -mcommon_thrd_yield(void); - -#if (defined(_MSC_VER) && !defined(_M_IX86)) || (defined(__LP64__) && __LP64__) -/* (64-bit intrinsics are only available in x64) */ -#ifdef _MSC_VER -DECL_ATOMIC_INTEGRAL(int64, __int64, __int64, 64) -#else -DECL_ATOMIC_STDINT(int64, __int64, 64) -#endif -#else -static BSON_INLINE int64_t -mcommon_atomic_int64_fetch(const int64_t volatile *val, enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int64_fetch_add((int64_t volatile *)val, 0, order); -} - -static BSON_INLINE int64_t -mcommon_atomic_int64_fetch_add(int64_t volatile *val, int64_t v, enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int64_fetch_add(val, v, order); -} - -static BSON_INLINE int64_t -mcommon_atomic_int64_fetch_sub(int64_t volatile *val, int64_t v, enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int64_fetch_add(val, -v, order); -} - -static BSON_INLINE int64_t -mcommon_atomic_int64_exchange(int64_t volatile *val, int64_t v, enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int64_exchange(val, v, order); -} - -static BSON_INLINE int64_t -mcommon_atomic_int64_compare_exchange_strong(int64_t volatile *val, - int64_t expect_value, - int64_t new_value, - enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int64_compare_exchange_strong(val, expect_value, new_value, order); -} - -static BSON_INLINE int64_t -mcommon_atomic_int64_compare_exchange_weak(int64_t volatile *val, - int64_t expect_value, - int64_t new_value, - enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int64_compare_exchange_weak(val, expect_value, new_value, order); -} -#endif - -#if defined(MCOMMON_EMULATE_INT32) -static BSON_INLINE int32_t -mcommon_atomic_int32_fetch(const int32_t volatile *val, enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int32_fetch_add((int32_t volatile *)val, 0, order); -} - -static BSON_INLINE int32_t -mcommon_atomic_int32_fetch_add(int32_t volatile *val, int32_t v, enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int32_fetch_add(val, v, order); -} - -static BSON_INLINE int32_t -mcommon_atomic_int32_fetch_sub(int32_t volatile *val, int32_t v, enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int32_fetch_add(val, -v, order); -} - -static BSON_INLINE int32_t -mcommon_atomic_int32_exchange(int32_t volatile *val, int32_t v, enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int32_exchange(val, v, order); -} - -static BSON_INLINE int32_t -mcommon_atomic_int32_compare_exchange_strong(int32_t volatile *val, - int32_t expect_value, - int32_t new_value, - enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int32_compare_exchange_strong(val, expect_value, new_value, order); -} - -static BSON_INLINE int32_t -mcommon_atomic_int32_compare_exchange_weak(int32_t volatile *val, - int32_t expect_value, - int32_t new_value, - enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int32_compare_exchange_weak(val, expect_value, new_value, order); -} -#endif /* MCOMMON_EMULATE_INT32 */ - -#if defined(MCOMMON_EMULATE_INT) -static BSON_INLINE int -mcommon_atomic_int_fetch(const int volatile *val, enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int_fetch_add((int volatile *)val, 0, order); -} - -static BSON_INLINE int -mcommon_atomic_int_fetch_add(int volatile *val, int v, enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int_fetch_add(val, v, order); -} - -static BSON_INLINE int -mcommon_atomic_int_fetch_sub(int volatile *val, int v, enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int_fetch_add(val, -v, order); -} - -static BSON_INLINE int -mcommon_atomic_int_exchange(int volatile *val, int v, enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int_exchange(val, v, order); -} - -static BSON_INLINE int -mcommon_atomic_int_compare_exchange_strong(int volatile *val, - int expect_value, - int new_value, - enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int_compare_exchange_strong(val, expect_value, new_value, order); -} - -static BSON_INLINE int -mcommon_atomic_int_compare_exchange_weak(int volatile *val, - int expect_value, - int new_value, - enum mcommon_memory_order order) -{ - return _mcommon_emul_atomic_int_compare_exchange_weak(val, expect_value, new_value, order); -} -#endif /* MCOMMON_EMULATE_INT */ - -static BSON_INLINE void * -mcommon_atomic_ptr_exchange(void *volatile *ptr, void *new_value, enum mcommon_memory_order ord) -{ -#if defined(MCOMMON_EMULATE_PTR) - return _mcommon_emul_atomic_ptr_exchange(ptr, new_value, ord); -#elif defined(MCOMMON_USE_LEGACY_GCC_ATOMICS) - /* The older __sync_val_compare_and_swap also takes oldval */ - DEF_ATOMIC_OP(_InterlockedExchangePointer, , __sync_val_compare_and_swap, ord, ptr, *ptr, new_value); -#else - DEF_ATOMIC_OP(_InterlockedExchangePointer, __atomic_exchange_n, , ord, ptr, new_value); -#endif -} - -static BSON_INLINE void * -mcommon_atomic_ptr_compare_exchange_strong(void *volatile *ptr, - void *expect, - void *new_value, - enum mcommon_memory_order ord) -{ - switch (ord) { - case mcommon_memory_order_release: - case mcommon_memory_order_acq_rel: - case mcommon_memory_order_seq_cst: - DEF_ATOMIC_CMPEXCH_STRONG(Pointer, , __ATOMIC_SEQ_CST, ptr, expect, new_value); - return expect; - case mcommon_memory_order_relaxed: - DEF_ATOMIC_CMPEXCH_STRONG(Pointer, MSVC_MEMORDER_SUFFIX(_nf), __ATOMIC_RELAXED, ptr, expect, new_value); - return expect; - case mcommon_memory_order_consume: - DEF_ATOMIC_CMPEXCH_STRONG(Pointer, MSVC_MEMORDER_SUFFIX(_acq), __ATOMIC_CONSUME, ptr, expect, new_value); - return expect; - case mcommon_memory_order_acquire: - DEF_ATOMIC_CMPEXCH_STRONG(Pointer, MSVC_MEMORDER_SUFFIX(_acq), __ATOMIC_ACQUIRE, ptr, expect, new_value); - return expect; - default: - BSON_UNREACHABLE("Invalid mcommon_memory_order value"); - } -} - - -static BSON_INLINE void * -mcommon_atomic_ptr_compare_exchange_weak(void *volatile *ptr, - void *expect, - void *new_value, - enum mcommon_memory_order ord) -{ - switch (ord) { - case mcommon_memory_order_release: - case mcommon_memory_order_acq_rel: - case mcommon_memory_order_seq_cst: - DEF_ATOMIC_CMPEXCH_WEAK(Pointer, , __ATOMIC_SEQ_CST, ptr, expect, new_value); - return expect; - case mcommon_memory_order_relaxed: - DEF_ATOMIC_CMPEXCH_WEAK(Pointer, MSVC_MEMORDER_SUFFIX(_nf), __ATOMIC_RELAXED, ptr, expect, new_value); - return expect; - case mcommon_memory_order_consume: - DEF_ATOMIC_CMPEXCH_WEAK(Pointer, MSVC_MEMORDER_SUFFIX(_acq), __ATOMIC_CONSUME, ptr, expect, new_value); - return expect; - case mcommon_memory_order_acquire: - DEF_ATOMIC_CMPEXCH_WEAK(Pointer, MSVC_MEMORDER_SUFFIX(_acq), __ATOMIC_ACQUIRE, ptr, expect, new_value); - return expect; - default: - BSON_UNREACHABLE("Invalid mcommon_memory_order value"); - } -} - - -static BSON_INLINE void * -mcommon_atomic_ptr_fetch(void *volatile const *ptr, enum mcommon_memory_order ord) -{ - return mcommon_atomic_ptr_compare_exchange_strong((void *volatile *)ptr, NULL, NULL, ord); -} - -#undef DECL_ATOMIC_STDINT -#undef DECL_ATOMIC_INTEGRAL -#undef DEF_ATOMIC_OP -#undef DEF_ATOMIC_CMPEXCH_STRONG -#undef DEF_ATOMIC_CMPEXCH_WEAK -#undef MSVC_MEMORDER_SUFFIX - -/** - * @brief Generate a full-fence memory barrier at the call site. - */ -static BSON_INLINE void -mcommon_atomic_thread_fence(void) -{ - BSON_IF_MSVC(MemoryBarrier();) - BSON_IF_GNU_LIKE(__sync_synchronize();) - MCOMMON_IF_GNU_LEGACY_ATOMICS(__sync_synchronize();) -} - -#ifdef MCOMMON_USE_LEGACY_GCC_ATOMICS -#undef BSON_IF_GNU_LIKE -#define BSON_IF_GNU_LIKE(...) __VA_ARGS__ -#endif -#undef MCOMMON_IF_GNU_LEGACY_ATOMICS -#undef MCOMMON_USE_LEGACY_GCC_ATOMICS - - -#undef MCOMMON_EMULATE_PTR -#undef MCOMMON_EMULATE_INT32 -#undef MCOMMON_EMULATE_INT - - -#endif /* MONGO_C_DRIVER_COMMON_ATOMIC_PRIVATE_H */ diff --git a/bsonjs/common/common-atomic.c b/bsonjs/common/common-atomic.c deleted file mode 100644 index 0b7fed5..0000000 --- a/bsonjs/common/common-atomic.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include - -#ifdef BSON_OS_UNIX -/* For sched_yield() */ -#include -#endif - -void -mcommon_thrd_yield(void) -{ - BSON_IF_WINDOWS(SwitchToThread();) - BSON_IF_POSIX(sched_yield();) -} - -/** - * Some platforms do not support compiler intrinsics for atomic operations. - * We emulate that here using a spin lock and regular arithmetic operations - */ -static int8_t gEmulAtomicLock = 0; - -static void -_lock_emul_atomic(void) -{ - int i; - if (mcommon_atomic_int8_compare_exchange_weak(&gEmulAtomicLock, 0, 1, mcommon_memory_order_acquire) == 0) { - /* Successfully took the spinlock */ - return; - } - /* Failed. Try taking ten more times, then begin sleeping. */ - for (i = 0; i < 10; ++i) { - if (mcommon_atomic_int8_compare_exchange_weak(&gEmulAtomicLock, 0, 1, mcommon_memory_order_acquire) == 0) { - /* Succeeded in taking the lock */ - return; - } - } - /* Still don't have the lock. Spin and yield */ - while (mcommon_atomic_int8_compare_exchange_weak(&gEmulAtomicLock, 0, 1, mcommon_memory_order_acquire) != 0) { - mcommon_thrd_yield(); - } -} - -static void -_unlock_emul_atomic(void) -{ - int64_t rv = mcommon_atomic_int8_exchange(&gEmulAtomicLock, 0, mcommon_memory_order_release); - BSON_ASSERT(rv == 1 && "Released atomic lock while not holding it"); -} - -int64_t -_mcommon_emul_atomic_int64_fetch_add(volatile int64_t *p, int64_t n, enum mcommon_memory_order _unused) -{ - int64_t ret; - - BSON_UNUSED(_unused); - - _lock_emul_atomic(); - ret = *p; - *p += n; - _unlock_emul_atomic(); - return ret; -} - -int64_t -_mcommon_emul_atomic_int64_exchange(volatile int64_t *p, int64_t n, enum mcommon_memory_order _unused) -{ - int64_t ret; - - BSON_UNUSED(_unused); - - _lock_emul_atomic(); - ret = *p; - *p = n; - _unlock_emul_atomic(); - return ret; -} - -int64_t -_mcommon_emul_atomic_int64_compare_exchange_strong(volatile int64_t *p, - int64_t expect_value, - int64_t new_value, - enum mcommon_memory_order _unused) -{ - int64_t ret; - - BSON_UNUSED(_unused); - - _lock_emul_atomic(); - ret = *p; - if (ret == expect_value) { - *p = new_value; - } - _unlock_emul_atomic(); - return ret; -} - -int64_t -_mcommon_emul_atomic_int64_compare_exchange_weak(volatile int64_t *p, - int64_t expect_value, - int64_t new_value, - enum mcommon_memory_order order) -{ - /* We're emulating. We can't do a weak version. */ - return _mcommon_emul_atomic_int64_compare_exchange_strong(p, expect_value, new_value, order); -} - - -int32_t -_mcommon_emul_atomic_int32_fetch_add(volatile int32_t *p, int32_t n, enum mcommon_memory_order _unused) -{ - int32_t ret; - - BSON_UNUSED(_unused); - - _lock_emul_atomic(); - ret = *p; - *p += n; - _unlock_emul_atomic(); - return ret; -} - -int32_t -_mcommon_emul_atomic_int32_exchange(volatile int32_t *p, int32_t n, enum mcommon_memory_order _unused) -{ - int32_t ret; - - BSON_UNUSED(_unused); - - _lock_emul_atomic(); - ret = *p; - *p = n; - _unlock_emul_atomic(); - return ret; -} - -int32_t -_mcommon_emul_atomic_int32_compare_exchange_strong(volatile int32_t *p, - int32_t expect_value, - int32_t new_value, - enum mcommon_memory_order _unused) -{ - int32_t ret; - - BSON_UNUSED(_unused); - - _lock_emul_atomic(); - ret = *p; - if (ret == expect_value) { - *p = new_value; - } - _unlock_emul_atomic(); - return ret; -} - -int32_t -_mcommon_emul_atomic_int32_compare_exchange_weak(volatile int32_t *p, - int32_t expect_value, - int32_t new_value, - enum mcommon_memory_order order) -{ - /* We're emulating. We can't do a weak version. */ - return _mcommon_emul_atomic_int32_compare_exchange_strong(p, expect_value, new_value, order); -} - - -int -_mcommon_emul_atomic_int_fetch_add(volatile int *p, int n, enum mcommon_memory_order _unused) -{ - int ret; - - BSON_UNUSED(_unused); - - _lock_emul_atomic(); - ret = *p; - *p += n; - _unlock_emul_atomic(); - return ret; -} - -int -_mcommon_emul_atomic_int_exchange(volatile int *p, int n, enum mcommon_memory_order _unused) -{ - int ret; - - BSON_UNUSED(_unused); - - _lock_emul_atomic(); - ret = *p; - *p = n; - _unlock_emul_atomic(); - return ret; -} - -int -_mcommon_emul_atomic_int_compare_exchange_strong(volatile int *p, - int expect_value, - int new_value, - enum mcommon_memory_order _unused) -{ - int ret; - - BSON_UNUSED(_unused); - - _lock_emul_atomic(); - ret = *p; - if (ret == expect_value) { - *p = new_value; - } - _unlock_emul_atomic(); - return ret; -} - -int -_mcommon_emul_atomic_int_compare_exchange_weak(volatile int *p, - int expect_value, - int new_value, - enum mcommon_memory_order order) -{ - /* We're emulating. We can't do a weak version. */ - return _mcommon_emul_atomic_int_compare_exchange_strong(p, expect_value, new_value, order); -} - -void * -_mcommon_emul_atomic_ptr_exchange(void *volatile *p, void *n, enum mcommon_memory_order _unused) -{ - void *ret; - - BSON_UNUSED(_unused); - - _lock_emul_atomic(); - ret = *p; - *p = n; - _unlock_emul_atomic(); - return ret; -} diff --git a/bsonjs/common/common-b64-private.h b/bsonjs/common/common-b64-private.h deleted file mode 100644 index 9561774..0000000 --- a/bsonjs/common/common-b64-private.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#ifndef MONGO_C_DRIVER_COMMON_B64_PRIVATE_H -#define MONGO_C_DRIVER_COMMON_B64_PRIVATE_H - -#include - -#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 -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 b64_pton (src, NULL, 0), which will read the src - * data and return an exact 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 -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 -mcommon_b64_pton(char const *src, uint8_t *target, size_t targsize); - -#endif /* MONGO_C_DRIVER_COMMON_B64_PRIVATE_H */ diff --git a/bsonjs/common/common-b64.c b/bsonjs/common/common-b64.c deleted file mode 100644 index e0cc21a..0000000 --- a/bsonjs/common/common-b64.c +++ /dev/null @@ -1,539 +0,0 @@ -/* - * Copyright (c) 1996, 1998 by Internet Software Consortium. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS - * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE - * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL - * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR - * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS - * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS - * SOFTWARE. - */ - -/* - * Portions Copyright (c) 1995 by International Business Machines, Inc. - * - * International Business Machines, Inc. (hereinafter called IBM) grants - * permission under its copyrights to use, copy, modify, and distribute this - * Software with or without fee, provided that the above copyright notice and - * all paragraphs of this notice appear in all copies, and that the name of IBM - * not be used in connection with the marketing of any product incorporating - * the Software or modifications thereof, without specific, written prior - * permission. - * - * To the extent it has a right to do so, IBM grants an immunity from suit - * under its patents, if any, for the use, sale or manufacture of products to - * the extent that such products are used for performing Domain Name System - * dynamic updates in TCP/IP networks by means of the Software. No immunity is - * granted for any product per se or for any other function of any product. - * - * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, - * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING - * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN - * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. - */ - -#include -#include - -#include - -#include -#include - -#define Assert(Cond) \ - if (!(Cond)) \ - abort() - -static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char Pad64 = '='; - -/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) - * The following encoding technique is taken from RFC 1521 by Borenstein - * and Freed. It is reproduced here in a slightly edited form for - * convenience. - * - * A 65-character subset of US-ASCII is used, enabling 6 bits to be - * represented per printable character. (The extra 65th character, "=", - * is used to signify a special processing function.) - * - * The encoding process represents 24-bit groups of input bits as output - * strings of 4 encoded characters. Proceeding from left to right, a - * 24-bit input group is formed by concatenating 3 8-bit input groups. - * These 24 bits are then treated as 4 concatenated 6-bit groups, each - * of which is translated into a single digit in the base64 alphabet. - * - * Each 6-bit group is used as an index into an array of 64 printable - * characters. The character referenced by the index is placed in the - * output string. - * - * Table 1: The Base64 Alphabet - * - * Value Encoding Value Encoding Value Encoding Value Encoding - * 0 A 17 R 34 i 51 z - * 1 B 18 S 35 j 52 0 - * 2 C 19 T 36 k 53 1 - * 3 D 20 U 37 l 54 2 - * 4 E 21 V 38 m 55 3 - * 5 F 22 W 39 n 56 4 - * 6 G 23 X 40 o 57 5 - * 7 H 24 Y 41 p 58 6 - * 8 I 25 Z 42 q 59 7 - * 9 J 26 a 43 r 60 8 - * 10 K 27 b 44 s 61 9 - * 11 L 28 c 45 t 62 + - * 12 M 29 d 46 u 63 / - * 13 N 30 e 47 v - * 14 O 31 f 48 w (pad) = - * 15 P 32 g 49 x - * 16 Q 33 h 50 y - * - * Special processing is performed if fewer than 24 bits are available - * at the end of the data being encoded. A full encoding quantum is - * always completed at the end of a quantity. When fewer than 24 input - * bits are available in an input group, zero bits are added (on the - * right) to form an integral number of 6-bit groups. Padding at the - * end of the data is performed using the '=' character. - * - * Since all base64 input is an integral number of octets, only the - * following cases can arise: - * - * (1) the final quantum of encoding input is an integral - * multiple of 24 bits; here, the final unit of encoded - * output will be an integral multiple of 4 characters - * with no "=" padding, - * (2) the final quantum of encoding input is exactly 8 bits; - * here, the final unit of encoded output will be two - * characters followed by two "=" padding characters, or - * (3) the final quantum of encoding input is exactly 16 bits; - * here, the final unit of encoded output will be three - * characters followed by one "=" padding character. - */ - -int -mcommon_b64_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) -{ - size_t datalength = 0; - uint8_t input[3]; - uint8_t output[4]; - - if (!target) { - return -1; - } - - // While we have at least three chars to read: - while (srclength > 2) { - input[0] = *src++; - input[1] = *src++; - input[2] = *src++; - srclength -= 3; - - output[0] = input[0] >> 2; - output[1] = (uint8_t)(((input[0] & 0x03) << 4) + (input[1] >> 4)); - output[2] = (uint8_t)(((input[1] & 0x0f) << 2) + (input[2] >> 6)); - output[3] = input[2] & 0x3f; - Assert(output[0] < 64); - Assert(output[1] < 64); - Assert(output[2] < 64); - Assert(output[3] < 64); - - if (datalength + 4 > targsize) { - return -1; - } - target[datalength++] = Base64[output[0]]; - target[datalength++] = Base64[output[1]]; - target[datalength++] = Base64[output[2]]; - target[datalength++] = Base64[output[3]]; - } - - /* Now we worry about padding. */ - if (0 != srclength) { - /* Get what's left. */ - input[0] = input[1] = input[2] = '\0'; - - memcpy(input, src, srclength); - output[0] = input[0] >> 2; - output[1] = (uint8_t)(((input[0] & 0x03) << 4) + (input[1] >> 4)); - output[2] = (uint8_t)(((input[1] & 0x0f) << 2) + (input[2] >> 6)); - Assert(output[0] < 64); - Assert(output[1] < 64); - Assert(output[2] < 64); - - if (datalength + 4 > targsize) { - return -1; - } - target[datalength++] = Base64[output[0]]; - target[datalength++] = Base64[output[1]]; - - if (srclength == 1) { - target[datalength++] = Pad64; - } else { - target[datalength++] = Base64[output[2]]; - } - target[datalength++] = Pad64; - } - - if (datalength >= targsize) { - return -1; - } - target[datalength] = '\0'; /* Returned value doesn't count \0. */ - return (int)datalength; -} - -/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) - The following encoding technique is taken from RFC 1521 by Borenstein - and Freed. It is reproduced here in a slightly edited form for - convenience. - - A 65-character subset of US-ASCII is used, enabling 6 bits to be - represented per printable character. (The extra 65th character, "=", - is used to signify a special processing function.) - - The encoding process represents 24-bit groups of input bits as output - strings of 4 encoded characters. Proceeding from left to right, a - 24-bit input group is formed by concatenating 3 8-bit input groups. - These 24 bits are then treated as 4 concatenated 6-bit groups, each - of which is translated into a single digit in the base64 alphabet. - - Each 6-bit group is used as an index into an array of 64 printable - characters. The character referenced by the index is placed in the - output string. - - Table 1: The Base64 Alphabet - - Value Encoding Value Encoding Value Encoding Value Encoding - 0 A 17 R 34 i 51 z - 1 B 18 S 35 j 52 0 - 2 C 19 T 36 k 53 1 - 3 D 20 U 37 l 54 2 - 4 E 21 V 38 m 55 3 - 5 F 22 W 39 n 56 4 - 6 G 23 X 40 o 57 5 - 7 H 24 Y 41 p 58 6 - 8 I 25 Z 42 q 59 7 - 9 J 26 a 43 r 60 8 - 10 K 27 b 44 s 61 9 - 11 L 28 c 45 t 62 + - 12 M 29 d 46 u 63 / - 13 N 30 e 47 v - 14 O 31 f 48 w (pad) = - 15 P 32 g 49 x - 16 Q 33 h 50 y - - Special processing is performed if fewer than 24 bits are available - at the end of the data being encoded. A full encoding quantum is - always completed at the end of a quantity. When fewer than 24 input - bits are available in an input group, zero bits are added (on the - right) to form an integral number of 6-bit groups. Padding at the - end of the data is performed using the '=' character. - - Since all base64 input is an integral number of octets, only the - following cases can arise: - - (1) the final quantum of encoding input is an integral - multiple of 24 bits; here, the final unit of encoded - output will be an integral multiple of 4 characters - with no "=" padding, - (2) the final quantum of encoding input is exactly 8 bits; - here, the final unit of encoded output will be two - characters followed by two "=" padding characters, or - (3) the final quantum of encoding input is exactly 16 bits; - here, the final unit of encoded output will be three - characters followed by one "=" padding character. - */ - -/* skips all whitespace anywhere. - converts characters, four at a time, starting at (or after) - src from base - 64 numbers into three 8 bit bytes in the target area. - it returns the number of data bytes stored at the target, or -1 on error. - */ - -static uint8_t mongoc_b64rmap[256]; - -static const uint8_t mongoc_b64rmap_special = 0xf0; -static const uint8_t mongoc_b64rmap_end = 0xfd; -static const uint8_t mongoc_b64rmap_space = 0xfe; -static const uint8_t mongoc_b64rmap_invalid = 0xff; - -/* initializing the reverse map isn't thread safe, do it in pthread_once */ -static BSON_ONCE_FUN(bson_b64_initialize_rmap) -{ - /* Null: end of string, stop parsing */ - mongoc_b64rmap[0] = mongoc_b64rmap_end; - - mlib_foreach_urange (i, 1, 256) { - const uint8_t ch = (uint8_t)i; - /* Whitespaces */ - if (bson_isspace(ch)) - mongoc_b64rmap[ch] = mongoc_b64rmap_space; - /* Padding: stop parsing */ - else if (ch == Pad64) - mongoc_b64rmap[ch] = mongoc_b64rmap_end; - /* Non-base64 char */ - else - mongoc_b64rmap[ch] = mongoc_b64rmap_invalid; - } - - /* Fill reverse mapping for base64 chars */ - for (uint8_t i = 0; Base64[i] != '\0'; ++i) - mongoc_b64rmap[(uint8_t)Base64[i]] = i; - - BSON_ONCE_RETURN; -} - -static int -mongoc_b64_pton_do(char const *src, uint8_t *target, size_t targsize) -{ - int tarindex, state; - uint8_t ch, ofs; - - state = 0; - tarindex = 0; - - while (1) { - ch = (uint8_t)*src++; - ofs = mongoc_b64rmap[ch]; - - if (ofs >= mongoc_b64rmap_special) { - /* Ignore whitespaces */ - if (ofs == mongoc_b64rmap_space) - continue; - /* End of base64 characters */ - if (ofs == mongoc_b64rmap_end) - break; - /* A non-base64 character. */ - return (-1); - } - - switch (state) { - case 0: - if ((size_t)tarindex >= targsize) - return (-1); - target[tarindex] = ofs << 2; - state = 1; - break; - case 1: - if ((size_t)tarindex + 1 >= targsize) - return (-1); - target[tarindex] |= ofs >> 4; - target[tarindex + 1] = (ofs & 0x0f) << 4; - tarindex++; - state = 2; - break; - case 2: - if ((size_t)tarindex + 1 >= targsize) - return (-1); - target[tarindex] |= ofs >> 2; - target[tarindex + 1] = (ofs & 0x03) << 6; - tarindex++; - state = 3; - break; - case 3: - if ((size_t)tarindex >= targsize) - return (-1); - target[tarindex] |= ofs; - tarindex++; - state = 0; - break; - default: - abort(); - } - } - - /* - * We are done decoding Base-64 chars. Let's see if we ended - * on a byte boundary, and/or with erroneous trailing characters. - */ - - if (ch == Pad64) { /* We got a pad char. */ - ch = (uint8_t)*src++; /* Skip it, get next. */ - switch (state) { - case 0: /* Invalid = in first position */ - case 1: /* Invalid = in second position */ - return (-1); - - case 2: /* Valid, means one byte of info */ - /* Skip any number of spaces. */ - for ((void)NULL; ch != '\0'; ch = (uint8_t)*src++) - if (mongoc_b64rmap[ch] != mongoc_b64rmap_space) - break; - /* Make sure there is another trailing = sign. */ - if (ch != Pad64) - return (-1); - ch = (uint8_t)*src++; /* Skip the = */ - /* Fall through to "single trailing =" case. */ - /* FALLTHROUGH */ - - case 3: /* Valid, means two bytes of info */ - /* - * We know this char is an =. Is there anything but - * whitespace after it? - */ - for ((void)NULL; ch != '\0'; ch = (uint8_t)*src++) - if (mongoc_b64rmap[ch] != mongoc_b64rmap_space) - return (-1); - - /* - * Now make sure for cases 2 and 3 that the "extra" - * bits that slopped past the last full byte were - * zeros. If we don't check them, they become a - * subliminal channel. - */ - if (target[tarindex] != 0) - return (-1); - default: - break; - } - } else { - /* - * We ended by seeing the end of the string. Make sure we - * have no partial bytes lying around. - */ - if (state != 0) - return (-1); - } - - return (tarindex); -} - - -static int -mongoc_b64_pton_len(char const *src) -{ - uint8_t ch = 0; - uint8_t ofs = 0; - int state = 0; - int tarindex = 0; - - while (1) { - ch = (uint8_t)*src++; - ofs = mongoc_b64rmap[ch]; - - if (ofs >= mongoc_b64rmap_special) { - /* Ignore whitespaces */ - if (ofs == mongoc_b64rmap_space) - continue; - /* End of base64 characters */ - if (ofs == mongoc_b64rmap_end) - break; - /* A non-base64 character. */ - return (-1); - } - - switch (state) { - case 0: - state = 1; - break; - case 1: - tarindex++; - state = 2; - break; - case 2: - tarindex++; - state = 3; - break; - case 3: - tarindex++; - state = 0; - break; - default: - abort(); - } - } - - /* - * We are done decoding Base-64 chars. Let's see if we ended - * on a byte boundary, and/or with erroneous trailing characters. - */ - - if (ch == Pad64) { /* We got a pad char. */ - ch = (uint8_t)*src++; /* Skip it, get next. */ - switch (state) { - case 0: /* Invalid = in first position */ - case 1: /* Invalid = in second position */ - return (-1); - - case 2: /* Valid, means one byte of info */ - /* Skip any number of spaces. */ - for ((void)NULL; ch != '\0'; ch = (uint8_t)*src++) - if (mongoc_b64rmap[ch] != mongoc_b64rmap_space) - break; - /* Make sure there is another trailing = sign. */ - if (ch != Pad64) - return (-1); - ch = (uint8_t)*src++; /* Skip the = */ - /* Fall through to "single trailing =" case. */ - /* FALLTHROUGH */ - - case 3: /* Valid, means two bytes of info */ - /* - * We know this char is an =. Is there anything but - * whitespace after it? - */ - for (; ch != '\0'; ch = (uint8_t)*src++) - if (mongoc_b64rmap[ch] != mongoc_b64rmap_space) - return (-1); - - default: - break; - } - } else { - /* - * We ended by seeing the end of the string. Make sure we - * have no partial bytes lying around. - */ - if (state != 0) - return (-1); - } - - return (tarindex); -} - - -int -mcommon_b64_pton(char const *src, uint8_t *target, size_t targsize) -{ - static bson_once_t once = BSON_ONCE_INIT; - - bson_once(&once, bson_b64_initialize_rmap); - - if (!src) { - return -1; - } - - if (target) - return mongoc_b64_pton_do(src, target, targsize); - else - return mongoc_b64_pton_len(src); -} - -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 - * to the nearest group of 6. */ - size_t num_b64_chars = (num_bits + 5) / 6; - /* Round to nearest set of four. */ - size_t num_b64_chars_with_padding = 4 * ((num_b64_chars + 3) / 4); - /* Add one for NULL byte. */ - return num_b64_chars_with_padding + 1; -} - -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 - * data. */ - size_t num_bits = base64_encoded_size * 6; - /* Round down to the nearest group of eight. */ - return num_bits / 8; -} diff --git a/bsonjs/common/common-bits-private.h b/bsonjs/common/common-bits-private.h deleted file mode 100644 index 3a2a00e..0000000 --- a/bsonjs/common/common-bits-private.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#ifndef MONGO_C_DRIVER_COMMON_BITS_PRIVATE_H -#define MONGO_C_DRIVER_COMMON_BITS_PRIVATE_H - -#include - - -// Round up to the next power of two uint32_t value. Saturates on overflow. -static BSON_INLINE uint32_t -mcommon_next_power_of_two_u32(uint32_t v) -{ - if (v == 0) { - return 1; - } - - // https://graphics.stanford.edu/%7Eseander/bithacks.html#RoundUpPowerOf2 - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; - - if (v == 0) { - return UINT32_MAX; - } else { - return v; - } -} - - -#endif /* MONGO_C_DRIVER_COMMON_BITS_PRIVATE_H */ diff --git a/bsonjs/common/common-bson-dsl-private.h b/bsonjs/common/common-bson-dsl-private.h deleted file mode 100644 index 748ba54..0000000 --- a/bsonjs/common/common-bson-dsl-private.h +++ /dev/null @@ -1,1298 +0,0 @@ -#include - -#ifndef MONGO_C_DRIVER_COMMON_BSON_DSL_PRIVATE_H -#define MONGO_C_DRIVER_COMMON_BSON_DSL_PRIVATE_H - -/** - * @file common-bson-dsl-private.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 - -#include - -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) { \ - mlib_diagnostic_push(); \ - mlib_gnu_warning_disable("-Wshadow"); \ - mlib_msvc_warning(disable : 4456); \ - } else \ - ((void)0) - -#define _bsonDSL_restoreWarnings() \ - if (1) { \ - mlib_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; \ - bool _bvHalt = false; \ - const bool _bvContinue = false; \ - const bool _bvBreak = false; \ - (void)_bvHalt; \ - (void)_bvContinue; \ - (void)_bvBreak; \ - _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(); \ - bool _bvHalt = false; \ - (void)_bvHalt; \ - _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 (mlib_in_range(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_unsafe_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_boolean(X) _bsonArrayAppendValue(boolean(X)) -#define _bsonValueOperation_boolean(b) _bsonValueOperation_bool(b) - -#define _bsonValueOperation_oid(o) \ - if (!bson_append_oid(_bsonBuildAppendArgs, (o))) { \ - bsonBuildError = "Error while appending oid(" _bsonDSL_str(o) ")"; \ - } else \ - ((void)0) -#define _bsonArrayOperation_oid(X) _bsonArrayAppendValue(oid(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 - -#define _bsonValueOperation_binary(SubType, Data, Len) \ - if (!bson_append_binary(_bsonBuildAppendArgs, (SubType), (Data), (Len))) { \ - bsonBuildError = "Error while appending binary(" _bsonDSL_str(Data) ")"; \ - } else \ - ((void)0) - -/// 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 -// Use `boolean`, not `bool`. `bool` may be defined as a macro to `_Bool` or `int`: -#define _bsonDSL_Type_boolean 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", ""); \ - bool _bvCaseMatched = false; \ - (void)_bvCaseMatched; \ - _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]"; \ - } \ - bool _bvBreak = false; \ - bool _bvContinue = false; \ - (void)_bvBreak; \ - (void)_bvContinue; \ - 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_ASSERT(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_ASSERT(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 { \ - /* Keep track of which elements have been visited based on their index*/ \ - uint64_t _bpVisitBits_static[4] = {0}; \ - const bson_t *_bpDoc = &(Doc); \ - uint64_t *_bpVisitBits = _bpVisitBits_static; \ - size_t _bpNumVisitBitInts = sizeof _bpVisitBits_static / sizeof(uint64_t); \ - bool _bpFoundElement = false; \ - (void)_bpDoc; \ - (void)_bpVisitBits; \ - (void)_bpNumVisitBitInts; \ - (void)_bpFoundElement; \ - _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_ARRAY_ALLOC0(new_num_visit_bit_ints, uint64_t); \ - 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_always true -#define _bsonPredicate_Condition_never 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_ASSERT(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_boolean(void) -{ - return bson_iter_as_bool(&bsonVisitIter); -} - -#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 // MONGO_C_DRIVER_COMMON_BSON_DSL_PRIVATE_H diff --git a/bsonjs/common/common-config.h b/bsonjs/common/common-config.h deleted file mode 100644 index a40df73..0000000 --- a/bsonjs/common/common-config.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef COMMON_CONFIG_H -#define COMMON_CONFIG_H - -#define MONGOC_ENABLE_DEBUG_ASSERTIONS 0 - -#if MONGOC_ENABLE_DEBUG_ASSERTIONS != 1 -# undef MONGOC_ENABLE_DEBUG_ASSERTIONS -#endif - -#endif diff --git a/bsonjs/common/common-json-private.h b/bsonjs/common/common-json-private.h deleted file mode 100644 index 132baac..0000000 --- a/bsonjs/common/common-json-private.h +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#ifndef MONGO_C_DRIVER_COMMON_JSON_PRIVATE_H -#define MONGO_C_DRIVER_COMMON_JSON_PRIVATE_H - -#include - -#define mcommon_iso8601_string_append COMMON_NAME(iso8601_string_append) -#define mcommon_json_append_escaped COMMON_NAME(json_append_escaped) -#define mcommon_json_append_value_double COMMON_NAME(json_append_value_double) -#define mcommon_json_append_value_decimal128 COMMON_NAME(json_append_value_decimal128) -#define mcommon_json_append_value_oid COMMON_NAME(json_append_value_oid) -#define mcommon_json_append_value_binary COMMON_NAME(json_append_value_binary) -#define mcommon_json_append_value_date_time COMMON_NAME(json_append_value_date_time) -#define mcommon_json_append_value_timestamp COMMON_NAME(json_append_value_timestamp) -#define mcommon_json_append_value_regex COMMON_NAME(json_append_value_regex) -#define mcommon_json_append_value_dbpointer COMMON_NAME(json_append_value_dbpointer) -#define mcommon_json_append_value_code COMMON_NAME(json_append_value_code) -#define mcommon_json_append_value_codewscope COMMON_NAME(json_append_value_codewscope) -#define mcommon_json_append_value_symbol COMMON_NAME(json_append_value_symbol) -#define mcommon_json_append_bson_values COMMON_NAME(json_append_bson_values) -#define mcommon_json_append_bson_document COMMON_NAME(json_append_bson_document) -#define mcommon_json_append_bson_array COMMON_NAME(json_append_bson_array) - -// Needed by libbson and common-json -#ifndef BSON_MAX_RECURSION -#define BSON_MAX_RECURSION 200 -#endif - -// Needed by libbson and common-json -#define BSON_REGEX_OPTIONS_SORTED "ilmsux" - -/** - * @brief Append an ISO 8601 formatted date, given 64-bit milliseconds since the epoch - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param msec_since_epoch Milliseconds since Jan 1 1970 UTC - * @returns true on success, false if this 'append' has exceeded its max length - */ -bool -mcommon_iso8601_string_append(mcommon_string_append_t *append, int64_t msec_since_epoch); - -/** - * @brief Append a UTF-8 string with all special characters escaped - * - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param str UTF-8 string to escape and append - * @param len Length of 'str' in bytes - * @param allow_nul true if internal "00" bytes or "C0 80" sequences should be encoded as "\u0000", false to treat - * them as invalid data - * @returns true on success, false if this 'append' has exceeded its max length or if we encountered invalid UTF-8 or - * disallowed NUL bytes in 'str' - * - * The string may include internal NUL characters. It does not need to be NUL terminated. - * The two-byte sequence "C0 80" is also interpreted as an internal NUL, for historical reasons. This sequence is - * considered invalid according to RFC3629. - */ -bool -mcommon_json_append_escaped(mcommon_string_append_t *append, const char *str, uint32_t len, bool allow_nul); - -/** - * @brief Append a comma separator string to appear between values - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @returns true on success, false if this 'append' has exceeded its max length - */ -static BSON_INLINE bool -mcommon_json_append_separator(mcommon_string_append_t *append) -{ - return mcommon_string_append(append, ", "); -} - -/** - * @brief Append a quoted and escaped key and key-value separator - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param str UTF-8 string to escape and append - * @param len Length of 'str' in bytes - * @returns true on success, false if this 'append' has exceeded its max length or if we encountered invalid UTF-8 or - * disallowed NUL bytes in 'str' - * - * See mcommon_json_append_escaped. NUL values in keys are never allowed. - */ -static BSON_INLINE bool -mcommon_json_append_key(mcommon_string_append_t *append, const char *str, uint32_t len) -{ - return mcommon_string_append(append, "\"") && mcommon_json_append_escaped(append, str, len, false) && - mcommon_string_append(append, "\" : "); -} - -/** - * @brief Append a quoted and escaped string - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param str UTF-8 string to escape and append - * @param len Length of 'str' in bytes - * @param allow_nul true if internal "00" bytes or "C0 80" sequences should be encoded as "\u0000", false to treat them - * as invalid data - * @returns true on success, false if this 'append' has exceeded its max length or if we encountered invalid UTF-8 or - * disallowed NUL bytes in 'str' - * - * See mcommon_json_append_escaped. - */ -static BSON_INLINE bool -mcommon_json_append_value_utf8(mcommon_string_append_t *append, const char *str, uint32_t len, bool allow_nul) -{ - return mcommon_string_append(append, "\"") && mcommon_json_append_escaped(append, str, len, allow_nul) && - mcommon_string_append(append, "\""); -} - -/** - * @brief Append an int32_t value, serialized according to a bson_json_mode_t - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param value Integer value - * @param mode One of the JSON serialization modes, as a bson_json_mode_t. - * @returns true on success, false if this 'append' has exceeded its max length - */ -static BSON_INLINE bool -mcommon_json_append_value_int32(mcommon_string_append_t *append, int32_t value, bson_json_mode_t mode) -{ - return mode == BSON_JSON_MODE_CANONICAL - ? mcommon_string_append_printf(append, "{ \"$numberInt\" : \"%" PRId32 "\" }", value) - : mcommon_string_append_printf(append, "%" PRId32, value); -} - -/** - * @brief Append an int64_t value, serialized according to a bson_json_mode_t - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param value Integer value - * @param mode One of the JSON serialization modes, as a bson_json_mode_t. - * @returns true on success, false if this 'append' has exceeded its max length - */ -static BSON_INLINE bool -mcommon_json_append_value_int64(mcommon_string_append_t *append, int64_t value, bson_json_mode_t mode) -{ - return mode == BSON_JSON_MODE_CANONICAL - ? mcommon_string_append_printf(append, "{ \"$numberLong\" : \"%" PRId64 "\" }", value) - : mcommon_string_append_printf(append, "%" PRId64, value); -} - -/** - * @brief Append a JSON compatible bool value - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param bool Boolean value - * @returns true on success, false if this 'append' has exceeded its max length - */ -static BSON_INLINE bool -mcommon_json_append_value_bool(mcommon_string_append_t *append, bool value) -{ - return mcommon_string_append(append, value ? "true" : "false"); -} - -/** - * @brief Append an $undefined value - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @returns true on success, false if this 'append' has exceeded its max length - */ -static BSON_INLINE bool -mcommon_json_append_value_undefined(mcommon_string_append_t *append) -{ - return mcommon_string_append(append, "{ \"$undefined\" : true }"); -} - -/** - * @brief Append a null value - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @returns true on success, false if this 'append' has exceeded its max length - */ -static BSON_INLINE bool -mcommon_json_append_value_null(mcommon_string_append_t *append) -{ - return mcommon_string_append(append, "null"); -} - -/** - * @brief Append a $minKey value - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @returns true on success, false if this 'append' has exceeded its max length - */ -static BSON_INLINE bool -mcommon_json_append_value_minkey(mcommon_string_append_t *append) -{ - return mcommon_string_append(append, "{ \"$minKey\" : 1 }"); -} - -/** - * @brief Append a $maxKey value - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @returns true on success, false if this 'append' has exceeded its max length - */ -static BSON_INLINE bool -mcommon_json_append_value_maxkey(mcommon_string_append_t *append) -{ - return mcommon_string_append(append, "{ \"$maxKey\" : 1 }"); -} - -/** - * @brief Append a double-precision floating point value - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param value Double-precision floating point value - * @param mode One of the JSON serialization modes, as a bson_json_mode_t. - * @returns true on success, false if this 'append' has exceeded its max length - */ -bool -mcommon_json_append_value_double(mcommon_string_append_t *append, double value, bson_json_mode_t mode); - -/** - * @brief Append a decimal128 value - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param value decimal128 value to copy - * @returns true on success, false if this 'append' has exceeded its max length - */ -bool -mcommon_json_append_value_decimal128(mcommon_string_append_t *append, const bson_decimal128_t *value); - -/** - * @brief Append the $oid JSON serialization of an ObjectId value - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param value bson_oid_t value to copy - * @returns true on success, false if this 'append' has exceeded its max length - */ -bool -mcommon_json_append_value_oid(mcommon_string_append_t *append, const bson_oid_t *value); - -/** - * @brief Append the JSON serialization of a BSON binary value - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param subtype Subtype code, identifying the format within the base64-encoded binary block - * @param bytes Bytes to be base64 encoded - * @param byte_count Number of bytes - * @param mode One of the JSON serialization modes, as a bson_json_mode_t - * @returns true on success, false if this 'append' has exceeded its max length - */ -bool -mcommon_json_append_value_binary(mcommon_string_append_t *append, - bson_subtype_t subtype, - const uint8_t *bytes, - uint32_t byte_count, - bson_json_mode_t mode); - -/** - * @brief Append the JSON serialization of a BSON date and time - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param msec_since_epoch Milliseconds since Jan 1 1970 - * @param mode One of the JSON serialization modes, as a bson_json_mode_t - * @returns true on success, false if this 'append' has exceeded its max length - */ -bool -mcommon_json_append_value_date_time(mcommon_string_append_t *append, int64_t msec_since_epoch, bson_json_mode_t mode); - -/** - * @brief Append the JSON serialization of a BSON timestamp value - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param timestamp 32-bit timestamp value - * @param increment 32-bit increment value - * @returns true on success, false if this 'append' has exceeded its max length - */ -bool -mcommon_json_append_value_timestamp(mcommon_string_append_t *append, uint32_t timestamp, uint32_t increment); - -/** - * @brief Append the JSON serialization of a BSON regular expression - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param pattern Regular expression pattern, as a UTF-8 string - * @param pattern_len Length of pattern string, in bytes - * @param options Regular expression options, as a UTF-8 string - * @param options_len Length of the options string, in bytes - * @param mode One of the JSON serialization modes, as a bson_json_mode_t - * @returns true on success, false if this 'append' has exceeded its max length - */ -bool -mcommon_json_append_value_regex(mcommon_string_append_t *append, - const char *pattern, - uint32_t pattern_len, - const char *options, - size_t options_len, - bson_json_mode_t mode); - -/** - * @brief Append the JSON serialization of a BSON legacy DBPointer - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param collection Collection name, as a UTF-8 string - * @param collection_len Length of collection name string, in bytes - * @param oid Optional ObjectId reference, or NULL - * @param mode One of the JSON serialization modes, as a bson_json_mode_t - * @returns true on success, false if this 'append' has exceeded its max length - */ -bool -mcommon_json_append_value_dbpointer(mcommon_string_append_t *append, - const char *collection, - uint32_t collection_len, - const bson_oid_t *oid, - bson_json_mode_t mode); - -/** - * @brief Append the JSON serialization of a BSON legacy code object - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param code Code string, in UTF-8 - * @param code_len Length of code string, in bytes - * @returns true on success, false if this 'append' has exceeded its max length - */ -bool -mcommon_json_append_value_code(mcommon_string_append_t *append, const char *code, uint32_t code_len); - -/** - * @brief Append the JSON serialization of a BSON legacy code-with-scope object - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param code Code string, in UTF-8 - * @param code_len Length of code string, in bytes - * @param scope Scope as a bson_t document - * @param mode One of the JSON serialization modes, as a bson_json_mode_t - * @param max_depth Maximum allowed number of document/array nesting levels below this one - * @returns true if the input bson was valid, even if we reached max length. false on invalid BSON. - */ -bool -mcommon_json_append_value_codewscope(mcommon_string_append_t *append, - const char *code, - uint32_t code_len, - const bson_t *scope, - bson_json_mode_t mode, - unsigned max_depth); - -/** - * @brief Append the JSON serialization of a BSON legacy symbol object - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param symbol Symbol string, in UTF-8 - * @param symbol_len Length of symbol string, in bytes - * @param mode One of the JSON serialization modes, as a bson_json_mode_t. - * @returns true on success, false if this 'append' has exceeded its max length - */ -bool -mcommon_json_append_value_symbol(mcommon_string_append_t *append, - const char *symbol, - uint32_t symbol_len, - bson_json_mode_t mode); - -/** - * @brief Append all JSON-serialized values from a bson_t - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param bson bson_t document or array - * @param mode One of the JSON serialization modes, as a bson_json_mode_t - * @param has_keys true if this is a document, false if this is an array - * @param max_depth Maximum allowed number of document/array nesting levels below this one - * @returns true if the input bson was valid, even if we reached max length. false on invalid BSON. - * - * This generates keys, values, and separators but does not enclose the result in {} or []. - * Note that the return value reflects the status of BSON decoding, not string appending. - * The append status can be read using mcommon_string_status_from_append() if needed. - * If encoding was stopped early due to the max depth limit or max length, invalid input may go unnoticed. - */ -bool -mcommon_json_append_bson_values( - mcommon_string_append_t *append, const bson_t *bson, bson_json_mode_t mode, bool has_keys, unsigned max_depth); - -/** - * @brief Append a BSON document serialized as a JSON document - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param bson bson_t document - * @param mode One of the JSON serialization modes, as a bson_json_mode_t - * @param max_depth Maximum allowed number of document/array nesting levels *including* this one. If zero, appends "{ - * ... }". - * @returns true if the input bson was valid, even if we reached max length. false on invalid BSON. - */ -bool -mcommon_json_append_bson_document(mcommon_string_append_t *append, - const bson_t *bson, - bson_json_mode_t mode, - unsigned max_depth); - -/** - * @brief Append a BSON document serialized as a JSON array - * @param append A bounded string append, initialized with mcommon_string_set_append() - * @param bson bson_t to interpret as an array - * @param mode One of the JSON serialization modes, as a bson_json_mode_t - * @param max_depth Maximum allowed number of document/array nesting levels *including* this one. If zero, appends "[ - * ... ]". - * @returns true if the input bson was valid, even if we reached max length. false on invalid BSON. - */ -bool -mcommon_json_append_bson_array(mcommon_string_append_t *append, - const bson_t *bson, - bson_json_mode_t mode, - unsigned max_depth); - -#endif /* MONGO_C_DRIVER_COMMON_JSON_PRIVATE_H */ diff --git a/bsonjs/common/common-json.c b/bsonjs/common/common-json.c deleted file mode 100644 index 2dda278..0000000 --- a/bsonjs/common/common-json.c +++ /dev/null @@ -1,765 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include -#include -#include - -#include - -#include - - -typedef struct { - mcommon_string_append_t *append; - unsigned max_depth; - bson_json_mode_t mode; - bool has_keys; - bool not_first_item; - bool is_corrupt; -} mcommon_json_append_visit_t; - - -static bool -mcommon_json_append_visit_utf8( - const bson_iter_t *iter, const char *key, size_t v_utf8_len, const char *v_utf8, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - if (!mlib_in_range(uint32_t, v_utf8_len)) { - mcommon_string_append_overflow(state->append); - return true; - } - return !mcommon_json_append_value_utf8(state->append, v_utf8, (uint32_t)v_utf8_len, true); -} - -static bool -mcommon_json_append_visit_int32(const bson_iter_t *iter, const char *key, int32_t v_int32, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - return !mcommon_json_append_value_int32(state->append, v_int32, state->mode); -} - -static bool -mcommon_json_append_visit_int64(const bson_iter_t *iter, const char *key, int64_t v_int64, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - return !mcommon_json_append_value_int64(state->append, v_int64, state->mode); -} - -static bool -mcommon_json_append_visit_decimal128(const bson_iter_t *iter, - const char *key, - const bson_decimal128_t *value, - void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - return !mcommon_json_append_value_decimal128(state->append, value); -} - -static bool -mcommon_json_append_visit_double(const bson_iter_t *iter, const char *key, double v_double, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - return !mcommon_json_append_value_double(state->append, v_double, state->mode); -} - -static bool -mcommon_json_append_visit_undefined(const bson_iter_t *iter, const char *key, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - return !mcommon_json_append_value_undefined(state->append); -} - -static bool -mcommon_json_append_visit_null(const bson_iter_t *iter, const char *key, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - return !mcommon_json_append_value_null(state->append); -} - -static bool -mcommon_json_append_visit_oid(const bson_iter_t *iter, const char *key, const bson_oid_t *oid, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - return !mcommon_json_append_value_oid(state->append, oid); -} - -static bool -mcommon_json_append_visit_binary(const bson_iter_t *iter, - const char *key, - bson_subtype_t v_subtype, - size_t v_binary_len, - const uint8_t *v_binary, - void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - if (!mlib_in_range(uint32_t, v_binary_len)) { - mcommon_string_append_overflow(state->append); - return true; - } - return !mcommon_json_append_value_binary(state->append, v_subtype, v_binary, (uint32_t)v_binary_len, state->mode); -} - -static bool -mcommon_json_append_visit_bool(const bson_iter_t *iter, const char *key, bool v_bool, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - return !mcommon_json_append_value_bool(state->append, v_bool); -} - -static bool -mcommon_json_append_visit_date_time(const bson_iter_t *iter, const char *key, int64_t msec_since_epoch, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - return !mcommon_json_append_value_date_time(state->append, msec_since_epoch, state->mode); -} - -static bool -mcommon_json_append_visit_regex( - const bson_iter_t *iter, const char *key, const char *v_regex, const char *v_options, void *data) -{ - mcommon_json_append_visit_t *state = data; - size_t v_regex_len = strlen(v_regex); - size_t v_options_len = strlen(v_options); - BSON_UNUSED(iter); - BSON_UNUSED(key); - if (!mlib_in_range(uint32_t, v_regex_len)) { - mcommon_string_append_overflow(state->append); - return true; - } - return !mcommon_json_append_value_regex( - state->append, v_regex, (uint32_t)v_regex_len, v_options, v_options_len, state->mode); -} - -static bool -mcommon_json_append_visit_timestamp( - const bson_iter_t *iter, const char *key, uint32_t v_timestamp, uint32_t v_increment, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - return !mcommon_json_append_value_timestamp(state->append, v_timestamp, v_increment); -} - -static bool -mcommon_json_append_visit_dbpointer(const bson_iter_t *iter, - const char *key, - size_t v_collection_len, - const char *v_collection, - const bson_oid_t *v_oid, - void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - if (!mlib_in_range(uint32_t, v_collection_len)) { - mcommon_string_append_overflow(state->append); - return true; - } - return !mcommon_json_append_value_dbpointer( - state->append, v_collection, (uint32_t)v_collection_len, v_oid, state->mode); -} - -static bool -mcommon_json_append_visit_minkey(const bson_iter_t *iter, const char *key, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - return !mcommon_json_append_value_minkey(state->append); -} - -static bool -mcommon_json_append_visit_maxkey(const bson_iter_t *iter, const char *key, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - return !mcommon_json_append_value_maxkey(state->append); -} - -static bool -mcommon_json_append_visit_before(const bson_iter_t *iter, const char *key, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - - if (!mcommon_string_status_from_append(state->append)) { - return true; - } - - if (state->not_first_item) { - if (!mcommon_json_append_separator(state->append)) { - return true; - } - } else { - state->not_first_item = true; - } - - if (state->has_keys) { - size_t key_len = strlen(key); - if (!mlib_in_range(uint32_t, key_len)) { - mcommon_string_append_overflow(state->append); - return true; - } - if (!mcommon_json_append_key(state->append, key, (uint32_t)key_len)) { - return true; - } - } - - return false; -} - -static bool -mcommon_json_append_visit_after(const bson_iter_t *iter, const char *key, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - return !mcommon_string_status_from_append(state->append); -} - -static void -mcommon_json_append_visit_corrupt(const bson_iter_t *iter, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - state->is_corrupt = true; -} - -static bool -mcommon_json_append_visit_code( - const bson_iter_t *iter, const char *key, size_t v_code_len, const char *v_code, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - if (!mlib_in_range(uint32_t, v_code_len)) { - mcommon_string_append_overflow(state->append); - return true; - } - return !mcommon_json_append_value_code(state->append, v_code, (uint32_t)v_code_len); -} - -static bool -mcommon_json_append_visit_symbol( - const bson_iter_t *iter, const char *key, size_t v_symbol_len, const char *v_symbol, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - if (!mlib_in_range(uint32_t, v_symbol_len)) { - mcommon_string_append_overflow(state->append); - return true; - } - return !mcommon_json_append_value_symbol(state->append, v_symbol, (uint32_t)v_symbol_len, state->mode); -} - -static bool -mcommon_json_append_visit_codewscope( - const bson_iter_t *iter, const char *key, size_t v_code_len, const char *v_code, const bson_t *v_scope, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - if (!mlib_in_range(uint32_t, v_code_len)) { - mcommon_string_append_overflow(state->append); - return true; - } - if (mcommon_json_append_value_codewscope( - state->append, v_code, (uint32_t)v_code_len, v_scope, state->mode, state->max_depth)) { - return !mcommon_string_status_from_append(state->append); - } else { - state->is_corrupt = true; - return true; - } -} - -static bool -mcommon_json_append_visit_document(const bson_iter_t *iter, const char *key, const bson_t *v_document, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - if (mcommon_json_append_bson_document(state->append, v_document, state->mode, state->max_depth)) { - return !mcommon_string_status_from_append(state->append); - } else { - state->is_corrupt = true; - return true; - } -} - -static bool -mcommon_json_append_visit_array(const bson_iter_t *iter, const char *key, const bson_t *v_array, void *data) -{ - mcommon_json_append_visit_t *state = data; - BSON_UNUSED(iter); - BSON_UNUSED(key); - if (mcommon_json_append_bson_array(state->append, v_array, state->mode, state->max_depth)) { - return !mcommon_string_status_from_append(state->append); - } else { - state->is_corrupt = true; - return true; - } -} - -bool -mcommon_json_append_bson_values( - mcommon_string_append_t *append, const bson_t *bson, bson_json_mode_t mode, bool has_keys, unsigned max_depth) -{ - mcommon_json_append_visit_t state = {.append = append, .max_depth = max_depth, .mode = mode, .has_keys = has_keys}; - bson_iter_t iter; - if (!bson_iter_init(&iter, bson)) { - return false; - } - static const bson_visitor_t visitors = { - mcommon_json_append_visit_before, mcommon_json_append_visit_after, mcommon_json_append_visit_corrupt, - mcommon_json_append_visit_double, mcommon_json_append_visit_utf8, mcommon_json_append_visit_document, - mcommon_json_append_visit_array, mcommon_json_append_visit_binary, mcommon_json_append_visit_undefined, - mcommon_json_append_visit_oid, mcommon_json_append_visit_bool, mcommon_json_append_visit_date_time, - mcommon_json_append_visit_null, mcommon_json_append_visit_regex, mcommon_json_append_visit_dbpointer, - mcommon_json_append_visit_code, mcommon_json_append_visit_symbol, mcommon_json_append_visit_codewscope, - mcommon_json_append_visit_int32, mcommon_json_append_visit_timestamp, mcommon_json_append_visit_int64, - mcommon_json_append_visit_maxkey, mcommon_json_append_visit_minkey, NULL, /* visit_unsupported_type */ - mcommon_json_append_visit_decimal128, - }; - /* Note that early exit from bson_iter_visit_all does not affect our success, which is based only on BSON validity. - * BSON errors will set is_corrupt if they prevent full traversal, but non-fatal parse errors (like invalid UTF-8) - * may let bson_iter_visit_all() succeed while leaving an error status in iter.err_off. */ - (void)bson_iter_visit_all(&iter, &visitors, &state); - return iter.err_off == 0 && !state.is_corrupt; -} - -static BSON_INLINE bool -mcommon_json_append_bson_container(mcommon_string_append_t *append, - const bson_t *bson, - bson_json_mode_t mode, - unsigned max_depth, - bool has_keys, - const char *empty, - const char *begin_non_empty, - const char *end_non_empty, - const char *omitted) -{ - // Note that the return value here is bson validity, not append status. - if (bson_empty(bson)) { - (void)mcommon_string_append(append, empty); - return true; - } else if (max_depth == 0) { - (void)mcommon_string_append(append, omitted); - return true; - } else { - (void)mcommon_string_append(append, begin_non_empty); - bool result = mcommon_json_append_bson_values(append, bson, mode, has_keys, max_depth - 1u); - (void)mcommon_string_append(append, end_non_empty); - return result; - } -} - -bool -mcommon_json_append_bson_document(mcommon_string_append_t *append, - const bson_t *bson, - bson_json_mode_t mode, - unsigned max_depth) -{ - return mcommon_json_append_bson_container(append, bson, mode, max_depth, true, "{ }", "{ ", " }", "{ ... }"); -} - -bool -mcommon_json_append_bson_array(mcommon_string_append_t *append, - const bson_t *bson, - bson_json_mode_t mode, - unsigned max_depth) -{ - return mcommon_json_append_bson_container(append, bson, mode, max_depth, false, "[ ]", "[ ", " ]", "[ ... ]"); -} - -/** - * @brief Like mcommon_string_append_printf (append, "\\u%04x", c) but intended to be more optimizable. - */ -static BSON_INLINE bool -mcommon_json_append_hex_char(mcommon_string_append_t *append, uint16_t c) -{ - static const char digit_table[] = "0123456789abcdef"; - char hex_char[6]; - hex_char[0] = '\\'; - hex_char[1] = 'u'; - hex_char[2] = digit_table[0xf & (c >> 12)]; - hex_char[3] = digit_table[0xf & (c >> 8)]; - hex_char[4] = digit_table[0xf & (c >> 4)]; - hex_char[5] = digit_table[0xf & c]; - return mcommon_string_append_bytes(append, hex_char, 6); -} - -/** - * @brief Test whether a byte may require special processing in mcommon_json_append_escaped. - * @returns true for bytes in the range 0x00 - 0x1F, '\\', '\"', and 0xC0. - */ -static BSON_INLINE bool -mcommon_json_append_escaped_considers_byte_as_special(uint8_t byte) -{ - static const uint64_t table[4] = { - 0x00000004ffffffffull, // 0x00-0x1F (control), 0x22 (") - 0x0000000010000000ull, // 0x5C (') - 0x0000000000000000ull, // none - 0x0000000000000001ull, // 0xC0 (Possible two-byte NUL) - }; - return 0 != (table[byte >> 6] & (1ull << (byte & 0x3f))); -} - -/** - * @brief Measure the number of consecutive non-special bytes. - */ -static BSON_INLINE uint32_t -mcommon_json_append_escaped_count_non_special_bytes(const char *str, uint32_t len) -{ - uint32_t result = 0; - // Good candidate for architecture-specific optimizations. - // SSE4 strcspn is nearly what we want, but our table of special bytes would be too large (34 > 16) - while (len) { - if (mcommon_json_append_escaped_considers_byte_as_special((uint8_t)*str)) { - break; - } - result++; - str++; - len--; - } - return result; -} - -bool -mcommon_json_append_escaped(mcommon_string_append_t *append, const char *str, uint32_t len, bool allow_nul) -{ - BSON_ASSERT_PARAM(append); - BSON_ASSERT_PARAM(str); - - // Repeatedly handle runs of zero or more non-special bytes punctuated by a potentially-special sequence. - uint32_t non_special_len = mcommon_json_append_escaped_count_non_special_bytes(str, len); - while (len) { - if (!mcommon_string_append_bytes(append, str, non_special_len)) { - return false; - } - str += non_special_len; - len -= non_special_len; - if (len) { - char c = *str; - switch (c) { - case '"': - if (!mcommon_string_append(append, "\\\"")) { - return false; - } - break; - case '\\': - if (!mcommon_string_append(append, "\\\\")) { - return false; - } - break; - case '\b': - if (!mcommon_string_append(append, "\\b")) { - return false; - } - break; - case '\f': - if (!mcommon_string_append(append, "\\f")) { - return false; - } - break; - case '\n': - if (!mcommon_string_append(append, "\\n")) { - return false; - } - break; - case '\r': - if (!mcommon_string_append(append, "\\r")) { - return false; - } - break; - case '\t': - if (!mcommon_string_append(append, "\\t")) { - return false; - } - break; - case '\0': - if (!allow_nul || !mcommon_json_append_hex_char(append, 0)) { - return false; - } - break; - case '\xc0': // Could be a 2-byte NUL, or could begin another non-special run - if (len >= 2 && str[1] == '\x80') { - if (!allow_nul || !mcommon_json_append_hex_char(append, 0)) { - return false; - } - str++; - len--; - } else { - // Wasn't "C0 80". Begin a non-special run with the "C0" byte, which is usually special. - non_special_len = mcommon_json_append_escaped_count_non_special_bytes(str + 1, len - 1) + 1; - continue; - } - break; - default: - BSON_ASSERT(c > 0x00 && c < 0x20); - if (!mcommon_json_append_hex_char(append, c)) { - return false; - } - break; - } - str++; - len--; - non_special_len = mcommon_json_append_escaped_count_non_special_bytes(str, len); - } - } - return mcommon_string_status_from_append(append); -} - -bool -mcommon_iso8601_string_append(mcommon_string_append_t *append, int64_t msec_since_epoch) -{ - time_t t; - int64_t msec_part; - char buf[64]; - - msec_part = msec_since_epoch % 1000; - t = (time_t)(msec_since_epoch / 1000); - -#ifdef BSON_HAVE_GMTIME_R - { - struct tm posix_date; - gmtime_r(&t, &posix_date); - strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%S", &posix_date); - } -#elif defined(_MSC_VER) - { - /* Windows gmtime_s is thread-safe */ - struct tm time_buf; - gmtime_s(&time_buf, &t); - strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%S", &time_buf); - } -#else - strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%S", gmtime(&t)); -#endif - - if (msec_part) { - return mcommon_string_append_printf(append, "%s.%03" PRId64 "Z", buf, msec_part); - } else { - return mcommon_string_append_printf(append, "%sZ", buf); - } -} - -bool -mcommon_json_append_value_double(mcommon_string_append_t *append, double value, bson_json_mode_t mode) -{ - /* 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. */ - bool legacy = - mode == BSON_JSON_MODE_LEGACY || (mode == BSON_JSON_MODE_RELAXED && !(value != value || value * 0 != 0)); - - if (!legacy) { - mcommon_string_append(append, "{ \"$numberDouble\" : \""); - } - - if (!legacy && value != value) { - mcommon_string_append(append, "NaN"); - } else if (!legacy && value * 0 != 0) { - if (value > 0) { - mcommon_string_append(append, "Infinity"); - } else { - mcommon_string_append(append, "-Infinity"); - } - } else { - const mcommon_string_t *string = mcommon_string_from_append(append); - uint32_t start_len = string->len; - if (mcommon_string_append_printf(append, "%.20g", value)) { - /* ensure trailing ".0" to distinguish "3" from "3.0" */ - if (strspn(&string->str[start_len], "0123456789-") == string->len - start_len) { - mcommon_string_append(append, ".0"); - } - } - } - - if (!legacy) { - mcommon_string_append(append, "\" }"); - } - - return mcommon_string_status_from_append(append); -} - -bool -mcommon_json_append_value_decimal128(mcommon_string_append_t *append, const bson_decimal128_t *value) -{ - char decimal128_string[BSON_DECIMAL128_STRING]; - bson_decimal128_to_string(value, decimal128_string); - - return mcommon_string_append(append, "{ \"$numberDecimal\" : \"") && - mcommon_string_append(append, decimal128_string) && mcommon_string_append(append, "\" }"); -} - -bool -mcommon_json_append_value_oid(mcommon_string_append_t *append, const bson_oid_t *value) -{ - return mcommon_string_append(append, "{ \"$oid\" : \"") && mcommon_string_append_oid_as_hex(append, value) && - mcommon_string_append(append, "\" }"); -} - -bool -mcommon_json_append_value_binary(mcommon_string_append_t *append, - bson_subtype_t subtype, - const uint8_t *bytes, - uint32_t byte_count, - bson_json_mode_t mode) -{ - if (mode == BSON_JSON_MODE_CANONICAL || mode == BSON_JSON_MODE_RELAXED) { - return mcommon_string_append(append, "{ \"$binary\" : { \"base64\" : \"") && - mcommon_string_append_base64_encode(append, bytes, byte_count) && - mcommon_string_append_printf(append, "\", \"subType\" : \"%02x\" } }", (unsigned int)subtype); - } else { - return mcommon_string_append(append, "{ \"$binary\" : \"") && - mcommon_string_append_base64_encode(append, bytes, byte_count) && - mcommon_string_append_printf(append, "\", \"$type\" : \"%02x\" }", (unsigned int)subtype); - } -} - -bool -mcommon_json_append_value_date_time(mcommon_string_append_t *append, int64_t msec_since_epoch, bson_json_mode_t mode) -{ - const int64_t y10k = 253402300800000; // 10000-01-01T00:00:00Z in milliseconds since the epoch. - - if (mode == BSON_JSON_MODE_CANONICAL || - (mode == BSON_JSON_MODE_RELAXED && (msec_since_epoch < 0 || msec_since_epoch >= y10k))) { - return mcommon_string_append_printf( - append, "{ \"$date\" : { \"$numberLong\" : \"%" PRId64 "\" } }", msec_since_epoch); - } else if (mode == BSON_JSON_MODE_RELAXED) { - return mcommon_string_append(append, "{ \"$date\" : \"") && - mcommon_iso8601_string_append(append, msec_since_epoch) && mcommon_string_append(append, "\" }"); - } else { - return mcommon_string_append_printf(append, "{ \"$date\" : %" PRId64 " }", msec_since_epoch); - } -} - -bool -mcommon_json_append_value_timestamp(mcommon_string_append_t *append, uint32_t timestamp, uint32_t increment) -{ - BSON_ASSERT_PARAM(append); - return mcommon_string_append_printf(append, "{ \"$timestamp\" : { \"t\" : %u, \"i\" : %u } }", timestamp, increment); -} - -bool -mcommon_json_append_value_regex(mcommon_string_append_t *append, - const char *pattern, - uint32_t pattern_len, - const char *options, - size_t options_len, - bson_json_mode_t mode) -{ - if (mode == BSON_JSON_MODE_CANONICAL || mode == BSON_JSON_MODE_RELAXED) { - return mcommon_string_append(append, "{ \"$regularExpression\" : { \"pattern\" : \"") && - mcommon_json_append_escaped(append, pattern, pattern_len, false) && - mcommon_string_append(append, "\", \"options\" : \"") && - mcommon_string_append_selected_chars(append, BSON_REGEX_OPTIONS_SORTED, options, options_len) && - mcommon_string_append(append, "\" } }"); - } else { - return mcommon_string_append(append, "{ \"$regex\" : \"") && - mcommon_json_append_escaped(append, pattern, pattern_len, false) && - mcommon_string_append(append, "\", \"$options\" : \"") && - mcommon_string_append_selected_chars(append, BSON_REGEX_OPTIONS_SORTED, options, options_len) && - mcommon_string_append(append, "\" }"); - } -} - -bool -mcommon_json_append_value_dbpointer(mcommon_string_append_t *append, - const char *collection, - uint32_t collection_len, - const bson_oid_t *oid, - bson_json_mode_t mode) -{ - if (mode == BSON_JSON_MODE_CANONICAL || mode == BSON_JSON_MODE_RELAXED) { - return mcommon_string_append(append, "{ \"$dbPointer\" : { \"$ref\" : \"") && - mcommon_json_append_escaped(append, collection, collection_len, false) && - mcommon_string_append(append, "\"") && - (!oid || (mcommon_string_append(append, ", \"$id\" : ") && mcommon_json_append_value_oid(append, oid))) && - mcommon_string_append(append, " } }"); - } else { - return mcommon_string_append(append, "{ \"$ref\" : \"") && - mcommon_json_append_escaped(append, collection, collection_len, false) && - mcommon_string_append(append, "\"") && - (!oid || - (mcommon_string_append(append, ", \"$id\" : \"") && mcommon_string_append_oid_as_hex(append, oid))) && - mcommon_string_append(append, "\" }"); - } -} - -bool -mcommon_json_append_value_code(mcommon_string_append_t *append, const char *code, uint32_t code_len) -{ - return mcommon_string_append(append, "{ \"$code\" : \"") && - mcommon_json_append_escaped(append, code, code_len, true) && mcommon_string_append(append, "\" }"); -} - -bool -mcommon_json_append_value_codewscope(mcommon_string_append_t *append, - const char *code, - uint32_t code_len, - const bson_t *scope, - bson_json_mode_t mode, - unsigned max_depth) -{ - // Note that the return value here is bson validity, not append status. - (void)mcommon_string_append(append, "{ \"$code\" : \""); - (void)mcommon_json_append_escaped(append, code, code_len, true); - (void)mcommon_string_append(append, "\", \"$scope\" : "); - bool result = mcommon_json_append_bson_document(append, scope, mode, max_depth); - (void)mcommon_string_append(append, " }"); - return result; -} - -bool -mcommon_json_append_value_symbol(mcommon_string_append_t *append, - const char *symbol, - uint32_t symbol_len, - bson_json_mode_t mode) -{ - if (mode == BSON_JSON_MODE_CANONICAL || mode == BSON_JSON_MODE_RELAXED) { - return mcommon_string_append(append, "{ \"$symbol\" : \"") && - mcommon_json_append_escaped(append, symbol, symbol_len, true) && mcommon_string_append(append, "\" }"); - } else { - return mcommon_json_append_value_utf8(append, symbol, symbol_len, true); - } -} diff --git a/bsonjs/common/common-macros-private.h b/bsonjs/common/common-macros-private.h deleted file mode 100644 index 068b094..0000000 --- a/bsonjs/common/common-macros-private.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#ifndef MONGO_C_DRIVER_COMMON_MACROS_PRIVATE_H -#define MONGO_C_DRIVER_COMMON_MACROS_PRIVATE_H - -/* Test only assert. Is a noop unless -DENABLE_DEBUG_ASSERTIONS=ON is set - * during configuration */ -#if defined(MONGOC_ENABLE_DEBUG_ASSERTIONS) && defined(BSON_OS_UNIX) -#define MONGOC_DEBUG_ASSERT(statement) BSON_ASSERT(statement) -#else -#define MONGOC_DEBUG_ASSERT(statement) ((void)0) -#endif - -#if defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) -#define MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") -#define MC_PRAGMA_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") -#elif defined(__clang__) -#define MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") -#define MC_PRAGMA_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") -#elif defined(_MSC_VER) -#define MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("warning ( push )") -#define MC_PRAGMA_DIAGNOSTIC_POP _Pragma("warning ( pop )") -#else -#define MC_PRAGMA_DIAGNOSTIC_PUSH -#define MC_PRAGMA_DIAGNOSTIC_POP -#endif - -// `MC_ENABLE_CONVERSION_WARNING_BEGIN` enables -Wconversion to check for potentially unsafe integer conversions. -// The `mcommon_in_range_*` functions can help address these warnings by ensuring a cast is within bounds. -#if defined(__GNUC__) -#define MC_ENABLE_CONVERSION_WARNING_BEGIN MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic warning \"-Wconversion\"") -#define MC_ENABLE_CONVERSION_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP -#elif defined(__clang__) -#define MC_ENABLE_CONVERSION_WARNING_BEGIN \ - MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("clang diagnostic warning \"-Wconversion\"") -#define MC_ENABLE_CONVERSION_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP -#else -#define MC_ENABLE_CONVERSION_WARNING_BEGIN -#define MC_ENABLE_CONVERSION_WARNING_END -#endif - -// Disable the -Wcast-function-type-strict warning. -#define MC_DISABLE_CAST_FUNCTION_TYPE_STRICT_WARNING_BEGIN -#define MC_DISABLE_CAST_FUNCTION_TYPE_STRICT_WARNING_END -#if defined(__clang__) -#if __has_warning("-Wcast-function-type-strict") -#undef MC_DISABLE_CAST_FUNCTION_TYPE_STRICT_WARNING_BEGIN -#undef MC_DISABLE_CAST_FUNCTION_TYPE_STRICT_WARNING_END -#define MC_DISABLE_CAST_FUNCTION_TYPE_STRICT_WARNING_BEGIN \ - MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("clang diagnostic ignored \"-Wcast-function-type-strict\"") -#define MC_DISABLE_CAST_FUNCTION_TYPE_STRICT_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP -#endif // __has_warning("-Wcast-function-type-strict") -#endif // defined(__clang__) - -#if defined(__GNUC__) -#define BEGIN_IGNORE_DEPRECATIONS \ - MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -#define END_IGNORE_DEPRECATIONS MC_PRAGMA_DIAGNOSTIC_POP -#elif defined(__clang__) -#define BEGIN_IGNORE_DEPRECATIONS \ - MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") -#define END_IGNORE_DEPRECATIONS MC_PRAGMA_DIAGNOSTIC_PUSH -#else -#define BEGIN_IGNORE_DEPRECATIONS -#define END_IGNORE_DEPRECATIONS -#endif - -// Disable the -Wimplicit warning (including -Wimplicit-int and -Wimplicit-function-declaration). -#if defined(__GNUC__) -#define MC_DISABLE_IMPLICIT_WARNING_BEGIN MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic ignored \"-Wimplicit\"") -#define MC_DISABLE_IMPLICIT_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP -#elif defined(__clang__) -#define MC_DISABLE_IMPLICIT_WARNING_BEGIN MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("clang diagnostic ignored \"-Wimplicit\"") -#define MC_DISABLE_IMPLICIT_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP -#elif defined(_MSC_VER) -#define MC_DISABLE_IMPLICIT_WARNING_BEGIN MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("warning (disable : 4013 4431)") -#define MC_DISABLE_IMPLICIT_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP -#else -#define MC_DISABLE_IMPLICIT_WARNING_BEGIN -#define MC_DISABLE_IMPLICIT_WARNING_END -#endif - -// Disable the -Wcast-qual warning -#if defined(__GNUC__) -#define MC_DISABLE_CAST_QUAL_WARNING_BEGIN MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") -#define MC_DISABLE_CAST_QUAL_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP -#elif defined(__clang__) -#define MC_DISABLE_CAST_QUAL_WARNING_BEGIN MC_PRAGMA_DIAGNOSTIC_PUSH _Pragma("clang diagnostic ignored \"-Wcast-qual\"") -#define MC_DISABLE_CAST_QUAL_WARNING_END MC_PRAGMA_DIAGNOSTIC_POP -#else -#define MC_DISABLE_CAST_QUAL_WARNING_BEGIN -#define MC_DISABLE_CAST_QUAL_WARNING_END -#endif - -#endif /* MONGO_C_DRIVER_COMMON_MACROS_PRIVATE_H */ diff --git a/bsonjs/common/common-md5-private.h b/bsonjs/common/common-md5-private.h deleted file mode 100644 index 2740a0e..0000000 --- a/bsonjs/common/common-md5-private.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#ifndef MONGO_C_DRIVER_COMMON_MD5_PRIVATE_H -#define MONGO_C_DRIVER_COMMON_MD5_PRIVATE_H - -#include - -BSON_BEGIN_DECLS - -typedef struct { - uint32_t count[2]; /* message length in bits, lsw first */ - uint32_t abcd[4]; /* digest buffer */ - uint8_t buf[64]; /* accumulate block */ -} bson_md5_t; - -#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 - -#endif /* MONGO_C_DRIVER_COMMON_MD5_PRIVATE_H */ diff --git a/bsonjs/common/common-oid-private.h b/bsonjs/common/common-oid-private.h deleted file mode 100644 index ca6a075..0000000 --- a/bsonjs/common/common-oid-private.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#ifndef MONGO_C_DRIVER_COMMON_OID_PRIVATE_H -#define MONGO_C_DRIVER_COMMON_OID_PRIVATE_H - -#include - -BSON_BEGIN_DECLS - -extern const bson_oid_t kZeroObjectId; - -void -mcommon_oid_set_zero(bson_oid_t *oid); - -bool -mcommon_oid_is_zero(const bson_oid_t *oid); - -BSON_END_DECLS - -#endif /* MONGO_C_DRIVER_COMMON_OID_PRIVATE_H */ diff --git a/bsonjs/common/common-oid.c b/bsonjs/common/common-oid.c deleted file mode 100644 index 21d3c5c..0000000 --- a/bsonjs/common/common-oid.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -const bson_oid_t kZeroObjectId = {{0}}; - -void -mcommon_oid_set_zero(bson_oid_t *oid) -{ - BSON_ASSERT(oid); - memset(oid, 0, sizeof *oid); -} - -bool -mcommon_oid_is_zero(const bson_oid_t *oid) -{ - BSON_ASSERT(oid); - return bson_oid_equal_unsafe(oid, &kZeroObjectId); -} diff --git a/bsonjs/common/common-prelude.h b/bsonjs/common/common-prelude.h deleted file mode 100644 index 62eabd4..0000000 --- a/bsonjs/common/common-prelude.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#if !defined(MONGOC_INSIDE) && !defined(MONGOC_COMPILATION) && !defined(BSON_COMPILATION) && !defined(BSON_INSIDE) -#error "Only or can be included directly." -#endif - -#define COMMON_NAME_1(a, b) COMMON_NAME_2(a, b) -#define COMMON_NAME_2(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 diff --git a/bsonjs/common/common-string-private.h b/bsonjs/common/common-string-private.h deleted file mode 100644 index 8f9e8b1..0000000 --- a/bsonjs/common/common-string-private.h +++ /dev/null @@ -1,673 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#ifndef MONGO_C_DRIVER_COMMON_STRING_PRIVATE_H -#define MONGO_C_DRIVER_COMMON_STRING_PRIVATE_H - -#include - -#include - -#include - - -/* - * In mcommon_string_t, 'str' is guaranteed to be NUL terminated and SHOULD be valid UTF-8. mcommon_string_t operations - * MUST maintain the validity of valid UTF-8 strings. - * - * Unused portions of the buffer may be uninitialized, and must not be compared or copied. - * - * 'len' is measured in bytes, not including the NUL terminator. - * - * 'alloc' is the actual length of the bson_malloc() allocation in bytes, including the required space for NUL - * termination. - * - * When we use 'capacity', it refers to the largest 'len' that the buffer could store. alloc == capacity + 1. - */ -typedef struct mcommon_string_t { - char *str; - uint32_t len; - uint32_t alloc; -} mcommon_string_t; - -/* Parameters and outcome for a bounded append operation on a mcommon_string_t. Individual type-specific append - * functions can consume this struct to communicate bounds info. "max_len_exceeded" can be tested any time an - * algorithmic exit is convenient; the actual appended content will be limited by max_len. Truncation is guaranteed not - * to split a valid UTF-8 byte sequence. - * - * Members are here to support inline definitions; not intended for direct access. - * - * Multiple mcommon_string_append_t may simultaneously refer to the same 'string' but this usage is not recommended. - * - * 'max_len_exceeded' only includes operations undertaken on this specific mcommon_string_append_t. It will not be set - * if the string was already overlong, or if a different mcommon_string_append_t experiences an overage. - */ -typedef struct mcommon_string_append_t { - mcommon_string_t *_string; - uint32_t _max_len; - bool _max_len_exceeded; -} mcommon_string_append_t; - -#define mcommon_string_new_with_capacity COMMON_NAME(string_new_with_capacity) -#define mcommon_string_new_with_buffer COMMON_NAME(string_new_with_buffer) -#define mcommon_string_destroy COMMON_NAME(string_destroy) -#define mcommon_string_destroy_with_steal COMMON_NAME(string_destroy_with_steal) -#define mcommon_string_grow_to_capacity COMMON_NAME(string_grow_to_capacity) -#define mcommon_string_append_selected_chars COMMON_NAME(string_append_selected_chars) -#define mcommon_string_append_bytes_internal COMMON_NAME(string_append_bytes_internal) -#define mcommon_string_append_bytes_all_or_none COMMON_NAME(string_append_bytes_all_or_none) -#define mcommon_string_append_unichar_internal COMMON_NAME(string_append_unichar_internal) -#define mcommon_string_append_base64_encode COMMON_NAME(string_append_base64_encode) -#define mcommon_string_append_oid_as_hex COMMON_NAME(string_append_oid_as_hex) -#define mcommon_string_append_printf COMMON_NAME(string_append_printf) -#define mcommon_string_append_vprintf COMMON_NAME(string_append_vprintf) - -bool -mcommon_string_append_bytes_internal(mcommon_string_append_t *append, const char *str, uint32_t len); - -bool -mcommon_string_append_unichar_internal(mcommon_string_append_t *append, bson_unichar_t unichar); - -/** - * @brief Allocate a new mcommon_string_t with a copy of the supplied initializer string and an explicit buffer - * capacity. - * - * @param str Initializer string, should be valid UTF-8. - * @param length Length of initializer string, in bytes. - * @param min_capacity Minimum string capacity, in bytes, the buffer must be able to store without reallocating. Does - * not include the NUL terminator. Must be less than UINT32_MAX. - * @returns A new mcommon_string_t that must be freed with mcommon_string_destroy() or - * mcommon_string_destroy_with_steal() and bson_free(). It will hold 'str' in its entirety, even if the requested - * min_capacity was smaller. - */ -mcommon_string_t * -mcommon_string_new_with_capacity(const char *str, uint32_t length, uint32_t min_capacity); - -/** - * @brief Allocate a new mcommon_string_t with a copy of the supplied initializer string and a minimum-capacity buffer - * - * @param str NUL terminated string, should be valid UTF-8. Must be less than UINT32_MAX bytes long, overlong input - * causes a runtime assertion failure. - * @returns A new mcommon_string_t that must be freed with mcommon_string_destroy() or - * mcommon_string_destroy_with_steal() and bson_free(). - */ -static BSON_INLINE mcommon_string_t * -mcommon_string_new(const char *str) -{ - BSON_ASSERT_PARAM(str); - size_t length = strlen(str); - BSON_ASSERT(mlib_in_range(uint32_t, length) && (uint32_t)length < UINT32_MAX); - return mcommon_string_new_with_capacity(str, (uint32_t)length, 0); -} - -/** - * @brief Allocate a new mcommon_string_t, taking ownership of an existing buffer - * - * @param buffer Buffer to adopt, suitable for bson_free() and bson_realloc(). - * @param length Length of the string data, in bytes, not including the required NUL terminator. If string data is - * present, it should be valid UTF-8. - * @param alloc Actual allocated size of the buffer, in bytes, including room for NUL termination. - * @returns A new mcommon_string_t that must be freed with mcommon_string_destroy() or - * mcommon_string_destroy_with_steal() and bson_free(). - */ -mcommon_string_t * -mcommon_string_new_with_buffer(char *buffer, uint32_t length, uint32_t alloc); - -/** - * @brief Deallocate a mcommon_string_t and its internal buffer - * @param string String allocated with mcommon_string_new, or NULL. - */ -void -mcommon_string_destroy(mcommon_string_t *string); - -/** - * @brief Deallocate a mcommon_string_t and return its internal buffer as a NUL-terminated C string. - * @param string String allocated with mcommon_string_new, or NULL. - * @returns A freestanding NUL-terminated string in a buffer that must be freed with bson_free(), or NULL if 'string' - * was NULL. - */ -char * -mcommon_string_destroy_with_steal(mcommon_string_t *string); - -/** - * @brief Truncate the string to zero length without deallocating the buffer - * @param string String to clear - */ -static BSON_INLINE void -mcommon_string_clear(mcommon_string_t *string) -{ - BSON_ASSERT_PARAM(string); - string->len = 0; - string->str[0] = '\0'; -} - -/** - * @brief Test if the string has zero length - * @param string String to test - */ -static BSON_INLINE bool -mcommon_string_is_empty(const mcommon_string_t *string) -{ - BSON_ASSERT_PARAM(string); - return string->len == 0; -} - -/** - * @brief Test if the string begins with a C string - * @param string mcommon_string_t to test - * @param substring prefix to match, as a NUL terminated C string. - */ -static BSON_INLINE bool -mcommon_string_starts_with_str(const mcommon_string_t *string, const char *substring) -{ - BSON_ASSERT_PARAM(string); - BSON_ASSERT_PARAM(substring); - - size_t substring_len = strlen(substring); - uint32_t string_len = string->len; - - if (mlib_in_range(uint32_t, substring_len) && (uint32_t)substring_len <= string_len) { - return 0 == memcmp(string->str, substring, substring_len); - } else { - return false; - } -} - -/** - * @brief Test if the string ends with a C string - * @param string mcommon_string_t to test - * @param substring suffix to match, as a NUL terminated C string. - */ -static BSON_INLINE bool -mcommon_string_ends_with_str(const mcommon_string_t *string, const char *substring) -{ - BSON_ASSERT_PARAM(string); - BSON_ASSERT_PARAM(substring); - - size_t substring_len = strlen(substring); - uint32_t string_len = string->len; - - if (mlib_in_range(uint32_t, substring_len) && (uint32_t)substring_len <= string_len) { - uint32_t offset = string_len - (uint32_t)substring_len; - return 0 == memcmp(string->str + offset, substring, substring_len); - } else { - return false; - } -} - -/** - * @brief Grow a mcommon_string_t buffer if necessary to ensure a minimum capacity - * - * @param string String allocated with mcommon_string_new - * @param capacity Minimum string length, in bytes, the buffer must be able to store without reallocating. Does not - * include the NUL terminator. Must be less than UINT32_MAX. - * - * If a reallocation is necessary, the actual allocation size will be chosen as the next highest power-of-two above the - * minimum needed to store 'capacity' as well as the NUL terminator. - */ -void -mcommon_string_grow_to_capacity(mcommon_string_t *string, uint32_t capacity); - -/** - * @brief Set an append operation for this string, with an explicit length limit - * @param string String allocated with mcommon_string_new - * @param new_append Pointer to an uninitialized mcommon_string_append_t - * @param max_len Maximum allowed length for the resulting string, in bytes. Must be less than UINT32_MAX. - * - * The mcommon_string_append_t does not need to be deallocated. It is no longer usable if the underlying - * mcommon_string_t is freed. - * - * If the string was already over maximum length, it will not be modified. All append operations are guaranteed not to - * lengthen the string beyond max_len. Truncations are guaranteed to happen at UTF-8 code point boundaries. - */ -static BSON_INLINE void -mcommon_string_set_append_with_limit(mcommon_string_t *string, mcommon_string_append_t *new_append, uint32_t max_len) -{ - BSON_ASSERT_PARAM(string); - BSON_ASSERT_PARAM(new_append); - BSON_ASSERT(max_len < UINT32_MAX); - - new_append->_string = string; - new_append->_max_len = max_len; - new_append->_max_len_exceeded = false; -} - -/** - * @brief Set an append operation for this string - * @param string String allocated with mcommon_string_new - * @param new_append Pointer to an uninitialized mcommon_string_append_t - * - * The mcommon_string_append_t does not need to be deallocated. It is no longer usable if the underlying - * mcommon_string_t is freed. - * - * The maximum string length will be set to the largest representable by the data type, UINT32_MAX - 1. - */ -static BSON_INLINE void -mcommon_string_set_append(mcommon_string_t *string, mcommon_string_append_t *new_append) -{ - BSON_ASSERT_PARAM(string); - BSON_ASSERT_PARAM(new_append); - - mcommon_string_set_append_with_limit(string, new_append, UINT32_MAX - 1u); -} - -/** - * @brief Allocate an empty mcommon_string_t with the specified initial capacity, and set an append operation for it - * with maximum length - * @param new_append Pointer to an uninitialized mcommon_string_append_t - * @param capacity Initial capacity for the string, in bytes, not including NUL termination - * - * Allocates a new mcommon_string_t, which will need to be deallocated by the caller. - * The mcommon_string_append_t itself does not need to be deallocated. - * - * The initial mcommon_string_t buffer will be allocated to have room for the given number of string bytes, not - * including the NUL terminator. The maximum append length will be set to the largest representable by the data type, - * UINT32_MAX - 1. - * - * This is a shortcut for mcommon_string_new_with_capacity() combined with mcommon_string_set_append(). - */ -static BSON_INLINE void -mcommon_string_new_with_capacity_as_append(mcommon_string_append_t *new_append, uint32_t capacity) -{ - BSON_ASSERT_PARAM(new_append); - - mcommon_string_set_append(mcommon_string_new_with_capacity("", 0, capacity), new_append); -} - -/** - * @brief Allocate an empty mcommon_string_t with default initial capacity, and set an append operation for it with - * maximum length - * @param new_append Pointer to an uninitialized mcommon_string_append_t - * - * Allocates a new mcommon_string_t, which will need to be deallocated by the caller. - * The mcommon_string_append_t itself does not need to be deallocated. - * - * The maximum string length will be set to the largest representable by the data type, UINT32_MAX - 1. - * The new string will be allocated with a small default capacity. - * - * This method is intended to be the most convenient way to start growing a string. If a reasonable guess - * can be made about the final size of the string, it's better to call mcommon_string_new_with_capacity_as_append() - * or mcommon_string_new_with_capacity() and mcommon_string_set_append(). - */ -static BSON_INLINE void -mcommon_string_new_as_append(mcommon_string_append_t *new_append) -{ - BSON_ASSERT_PARAM(new_append); - - mcommon_string_new_with_capacity_as_append(new_append, 32); -} - -/** - * @brief Begin appending to a new empty mcommon_string_t with a given capacity and a matching max append length. - * @param new_append Pointer to an uninitialized mcommon_string_append_t - * @param capacity Fixed capacity for the string, in bytes, not including NUL termination - * - * Allocates a new mcommon_string_t, which will need to be deallocated by the caller. - * The mcommon_string_append_t itself does not need to be deallocated. - * The string buffer will not need to resize for operations performed through the resulting mcommon_string_append_t. - */ -static BSON_INLINE void -mcommon_string_new_as_fixed_capacity_append(mcommon_string_append_t *new_append, uint32_t capacity) -{ - BSON_ASSERT_PARAM(new_append); - - mcommon_string_set_append_with_limit(mcommon_string_new_with_capacity("", 0, capacity), new_append, capacity); -} - -/** - * @brief Check the status of an append operation. - * @param append Append operation, initialized with mcommon_string_set_append - * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. - */ -static BSON_INLINE bool -mcommon_string_status_from_append(const mcommon_string_append_t *append) -{ - BSON_ASSERT_PARAM(append); - - return !append->_max_len_exceeded; -} - -/** - * @brief Get a mcommon_string_t pointer to a mcommon_string_append_t destination. - * @param append Append operation, initialized with mcommon_string_set_append - * @returns Pointer to the mcommon_string_t destination. - * - * The mcommon_string_append_t includes a plain mcommon_string_t pointer with no fixed ownership semantics. - * Depending on usage, it may be a string with borrowed ownership or the append operation may be its primary owner. - */ -static BSON_INLINE mcommon_string_t * -mcommon_string_from_append(const mcommon_string_append_t *append) -{ - BSON_ASSERT_PARAM(append); - - return append->_string; -} - -/** - * @brief Get the current string buffer for an mcommon_string_append_t destination. - * @param append Append operation, initialized with mcommon_string_set_append - * @returns String buffer pointer, NUL terminated, invalidated if the string is destroyed and by any operation that may - * grow the string. - * - * Shortcut for mcommon_string_from_append(append)->str - */ -static BSON_INLINE char * -mcommon_str_from_append(const mcommon_string_append_t *append) -{ - BSON_ASSERT_PARAM(append); - - return mcommon_string_from_append(append)->str; -} - -/** - * @brief Get the current string length for an mcommon_string_append_t destination. - * @param append Append operation, initialized with mcommon_string_set_append - * @returns Snapshot of the current string length - * - * Shortcut for mcommon_string_from_append(append)->len - */ -static BSON_INLINE uint32_t -mcommon_strlen_from_append(const mcommon_string_append_t *append) -{ - BSON_ASSERT_PARAM(append); - - return mcommon_string_from_append(append)->len; -} - -/** - * @brief Deallocate the mcommon_string_t destination associated with an mcommon_string_append_t - * @param append Append operation, initialized with mcommon_string_set_append - * The append operation will no longer be usable after this call. - */ -static BSON_INLINE void -mcommon_string_from_append_destroy(const mcommon_string_append_t *append) -{ - BSON_ASSERT_PARAM(append); - - mcommon_string_destroy(mcommon_string_from_append(append)); -} - -/** - * @brief Truncate the append destination string to zero length without deallocating its buffer. - * @param append Append operation, initialized with mcommon_string_set_append - * This is equivalent to mcommon_string_clear() combined with mcommon_string_from_append(). - */ -static BSON_INLINE void -mcommon_string_from_append_clear(const mcommon_string_append_t *append) -{ - BSON_ASSERT_PARAM(append); - - mcommon_string_clear(mcommon_string_from_append(append)); -} - -/** - * @brief Deallocate the mcommon_string_t destination associated with an mcommon_string_append_t and return its internal - * buffer - * @param append Append operation, initialized with mcommon_string_set_append - * @returns A freestanding NUL-terminated string in a buffer that must be freed with bson_free() - * The append operation will no longer be usable after this call. - */ -static BSON_INLINE char * -mcommon_string_from_append_destroy_with_steal(const mcommon_string_append_t *append) -{ - BSON_ASSERT_PARAM(append); - - return mcommon_string_destroy_with_steal(mcommon_string_from_append(append)); -} - -/** - * @brief Test if the append destination ends with a C string - * @param string mcommon_string_append_t with the string to test - * @param substring suffix to match, as a NUL terminated C string. - */ -static BSON_INLINE bool -mcommon_string_from_append_ends_with_str(const mcommon_string_append_t *append, const char *substring) -{ - BSON_ASSERT_PARAM(append); - BSON_ASSERT_PARAM(substring); - - return mcommon_string_ends_with_str(mcommon_string_from_append(append), substring); -} - -/** - * @brief Test if the append destination has zero length - * @param string mcommon_string_append_t with the string to test - */ -static BSON_INLINE bool -mcommon_string_from_append_is_empty(const mcommon_string_append_t *append) -{ - BSON_ASSERT_PARAM(append); - - return mcommon_string_is_empty(mcommon_string_from_append(append)); -} - -/** - * @brief Signal an explicit overflow during string append - * @param append Append operation, initialized with mcommon_string_set_append - * - * Future calls to mcommon_string_status_from_append() return false, exactly as if an overlong append was attempted and - * failed. This should be used for cases when a logical overflow is occurring but it was detected early enough that no - * actual append was attempted. - */ -static BSON_INLINE void -mcommon_string_append_overflow(mcommon_string_append_t *append) -{ - BSON_ASSERT_PARAM(append); - - append->_max_len_exceeded = true; -} - -/** - * @brief Append selected characters from a template - * @param append Append operation, initialized with mcommon_string_set_append - * @param template UTF-8 string listing allowed characters in the desired order - * @param selector UTF-8 string that chooses which template characters are appended - * @param selector_len Length of the selector string, in bytes - * - * Sort and filter lists of option characters. The template should list all allowed options in their desired order. - * This implementation does not support multi-byte template characters. ASSERTs that each template character is <= - * '\x7f'. Selectors may contain untrusted data, template should not. - */ -bool -mcommon_string_append_selected_chars(mcommon_string_append_t *append, - const char *template_, - const char *selector, - size_t selector_len); - -/** - * @brief Append a string with known length to the mcommon_string_t - * @param append Append operation, initialized with mcommon_string_set_append - * @param str String to append a copy of, should be valid UTF-8 - * @param len Length of 'str', in bytes - * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. - * - * If the string must be truncated to fit in the limit set by mcommon_string_set_append_with_limit, it will always be - * split in-between UTF-8 code points. - */ -static BSON_INLINE bool -mcommon_string_append_bytes(mcommon_string_append_t *append, const char *str, uint32_t len) -{ - BSON_ASSERT_PARAM(append); - BSON_ASSERT_PARAM(str); - - if (BSON_UNLIKELY(!mcommon_string_status_from_append(append))) { - return false; - } - - mcommon_string_t *string = append->_string; - char *buffer = string->str; - uint64_t alloc = (uint64_t)string->alloc; - uint64_t old_len = (uint64_t)string->len; - uint64_t max_len = (uint64_t)append->_max_len; - uint64_t new_len = old_len + (uint64_t)len; - uint64_t new_len_with_nul = new_len + 1; - - // Fast path: no truncation, no buffer growing - if (BSON_LIKELY(new_len <= max_len && new_len_with_nul <= alloc)) { - memcpy(buffer + old_len, str, len); - buffer[new_len] = '\0'; - string->len = (uint32_t)new_len; - return true; - } - - // Other cases are not inlined - return mcommon_string_append_bytes_internal(append, str, len); -} - -/** - * @brief Append a NUL-terminated UTF-8 string to the mcommon_string_t - * @param append Append operation, initialized with mcommon_string_set_append - * @param str NUL-terminated string to append a copy of - * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. - * - * If the string must be truncated to fit in the limit set by mcommon_string_set_append_with_limit, it will always be - * split in-between UTF-8 code points. - */ -static BSON_INLINE bool -mcommon_string_append(mcommon_string_append_t *append, const char *str) -{ - BSON_ASSERT_PARAM(append); - BSON_ASSERT_PARAM(str); - - return mcommon_string_append_bytes(append, str, strlen(str)); -} - -/** - * @brief Append an entire string with known length to the mcommon_string_t or fail, without truncating. - * @param append Append operation, initialized with mcommon_string_set_append - * @param str UTF-8 string to append a copy of - * @param len Length of 'str', in bytes - * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. - * - * Atomic version of mcommon_string_append_bytes. If string does not fit completely, it is not truncated. - * The destination string is only modified if the entire append operation can be completed. - */ -bool -mcommon_string_append_bytes_all_or_none(mcommon_string_append_t *append, const char *str, uint32_t len); - -/** - * @brief Append an entire NUL-terminated UTF-8 string to the mcommon_string_t or fail, without truncating. - * @param append Append operation, initialized with mcommon_string_set_append - * @param str NUL-terminated UTF-8 sequence to append a copy of - * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. - * - * Atomic version of mcommon_string_append. If string does not fit completely, it is not truncated. - * The destination string is only modified if the entire append operation can be completed. - */ -static BSON_INLINE bool -mcommon_string_append_all_or_none(mcommon_string_append_t *append, const char *str) -{ - BSON_ASSERT_PARAM(append); - BSON_ASSERT_PARAM(str); - - return mcommon_string_append_bytes_all_or_none(append, str, strlen(str)); -} - -/** - * @brief Append base64 encoded bytes to an mcommon_string_t - * @param append Append operation, initialized with mcommon_string_set_append - * @param bytes Bytes to be encoded - * @param len Number of bytes to encoded - * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. - */ -bool -mcommon_string_append_base64_encode(mcommon_string_append_t *append, const uint8_t *bytes, uint32_t len); - -/** - * @brief Append an ObjectId as a hex string - * @param append Append operation, initialized with mcommon_string_set_append - * @param value bson_oid_t value to copy - * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. - */ -bool -mcommon_string_append_oid_as_hex(mcommon_string_append_t *append, const bson_oid_t *value); - -/** - * @brief Append printf() formatted text to a mcommon_string_t - * @param append Append operation, initialized with mcommon_string_set_append - * @param format printf() format string - * @param ... Format string arguments - * @returns true if the append operation has no permanent error status, and this operation has succeeded. false if the - * max length has been surpassed or this printf() experienced an unrecoverable error. - * - * Writes the printf() result directly into the mcommon_string_t buffer, growing it as needed. - * - * If the string must be truncated to fit in the limit set by mcommon_string_set_append_with_limit, it will always be - * split in-between UTF-8 code points. - */ -bool -mcommon_string_append_printf(mcommon_string_append_t *append, const char *format, ...) BSON_GNUC_PRINTF(2, 3); - -/** - * @brief Variant of mcommon_string_append_printf() that takes a va_list - * @param append Append operation, initialized with mcommon_string_set_append - * @param format printf() format string - * @param args Format string arguments - * @returns true if the append operation has no permanent error status, and this operation has succeeded. false if the - * max length has been surpassed or this printf() experienced an unrecoverable error. - * - * Writes the printf() result directly into the mcommon_string_t buffer, growing it as needed. - * - * If the string must be truncated to fit in the limit set by mcommon_string_set_append_with_limit, it will always be - * split in-between UTF-8 code points. - */ -bool -mcommon_string_append_vprintf(mcommon_string_append_t *append, const char *format, va_list args) BSON_GNUC_PRINTF(2, 0); - -/** - * @brief Append one code point to a mcommon_string_t - * @param append Append operation, initialized with mcommon_string_set_append - * @param unichar Code point to append, as a bson_unichar_t - * @returns true if the append operation has no permanent error status. false if the max length has been exceeded. - * - * Guaranteed not to truncate. The character will fully append or no change will be made. - */ -static BSON_INLINE bool -mcommon_string_append_unichar(mcommon_string_append_t *append, bson_unichar_t unichar) -{ - BSON_ASSERT_PARAM(append); - - if (BSON_UNLIKELY(!mcommon_string_status_from_append(append))) { - return false; - } - - mcommon_string_t *string = append->_string; - BSON_ASSERT(string); - char *buffer = string->str; - uint64_t alloc = (uint64_t)string->alloc; - uint64_t old_len = (uint64_t)string->len; - uint64_t max_len = (uint64_t)append->_max_len; - - // Fast path: single-byte character, no truncation, no buffer growing - if (BSON_LIKELY(unichar <= 0x7f)) { - uint64_t new_len = old_len + 1; - uint64_t new_len_with_nul = new_len + 1; - if (BSON_LIKELY(new_len <= max_len && new_len_with_nul <= alloc)) { - buffer[old_len] = (char)unichar; - buffer[new_len] = '\0'; - string->len = new_len; - return true; - } - } - - // Other cases are not inlined - return mcommon_string_append_unichar_internal(append, unichar); -} - - -#endif /* MONGO_C_DRIVER_COMMON_STRING_PRIVATE_H */ diff --git a/bsonjs/common/common-string.c b/bsonjs/common/common-string.c deleted file mode 100644 index 8aae2a0..0000000 --- a/bsonjs/common/common-string.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include - -#include - - -mcommon_string_t * -mcommon_string_new_with_capacity(const char *str, uint32_t length, uint32_t min_capacity) -{ - BSON_ASSERT_PARAM(str); - BSON_ASSERT(length < UINT32_MAX && min_capacity < UINT32_MAX); - uint32_t capacity = BSON_MAX(length, min_capacity); - uint32_t alloc = capacity + 1u; - char *buffer = bson_malloc(alloc); - memcpy(buffer, str, length); - buffer[length] = '\0'; - return mcommon_string_new_with_buffer(buffer, length, alloc); -} - -mcommon_string_t * -mcommon_string_new_with_buffer(char *buffer, uint32_t length, uint32_t alloc) -{ - BSON_ASSERT_PARAM(buffer); - BSON_ASSERT(length < UINT32_MAX && alloc >= length + 1u); - BSON_ASSERT(buffer[length] == '\0'); - mcommon_string_t *string = bson_malloc0(sizeof *string); - string->str = buffer; - string->len = length; - string->alloc = alloc; - return string; -} - -void -mcommon_string_destroy(mcommon_string_t *string) -{ - if (string) { - bson_free(mcommon_string_destroy_with_steal(string)); - } -} - -char * -mcommon_string_destroy_with_steal(mcommon_string_t *string) -{ - if (string) { - char *buffer = string->str; - BSON_ASSERT(buffer[string->len] == '\0'); - bson_free(string); - return buffer; - } else { - return NULL; - } -} - -void -mcommon_string_grow_to_capacity(mcommon_string_t *string, uint32_t capacity) -{ - BSON_ASSERT_PARAM(string); - BSON_ASSERT(capacity < UINT32_MAX); - uint32_t min_alloc_needed = capacity + 1u; - if (string->alloc < min_alloc_needed) { - uint32_t alloc = mcommon_next_power_of_two_u32(min_alloc_needed); - string->str = bson_realloc(string->str, alloc); - string->alloc = alloc; - } -} - -// Handle cases omitted from the inlined mcommon_string_append_bytes() -bool -mcommon_string_append_bytes_internal(mcommon_string_append_t *append, const char *str, uint32_t len) -{ - mcommon_string_t *string = append->_string; - BSON_ASSERT(string); - uint32_t old_len = string->len; - uint32_t max_len = append->_max_len; - BSON_ASSERT(max_len < UINT32_MAX); - - uint32_t max_append_len = old_len < max_len ? max_len - old_len : 0; - uint32_t truncated_append_len = len; - if (len > max_append_len) { - // Search for an actual append length, <= the maximum allowed, which preserves UTF-8 validity - append->_max_len_exceeded = true; - truncated_append_len = mcommon_utf8_truncate_len(str, max_append_len); - } - - uint32_t new_len = old_len + truncated_append_len; - BSON_ASSERT(new_len <= max_len); - mcommon_string_grow_to_capacity(string, new_len); - char *buffer = string->str; - - memcpy(buffer + old_len, str, truncated_append_len); - buffer[new_len] = '\0'; - string->len = new_len; - - return mcommon_string_status_from_append(append); -} - -// Variant of mcommon_string_append_bytes() that grows but never truncates -bool -mcommon_string_append_bytes_all_or_none(mcommon_string_append_t *append, const char *str, uint32_t len) -{ - BSON_ASSERT_PARAM(append); - BSON_ASSERT_PARAM(str); - - if (BSON_UNLIKELY(!mcommon_string_status_from_append(append))) { - return false; - } - - mcommon_string_t *string = append->_string; - BSON_ASSERT(string); - uint32_t old_len = string->len; - uint32_t max_len = append->_max_len; - BSON_ASSERT(max_len < UINT32_MAX); - - uint32_t max_append_len = old_len < max_len ? max_len - old_len : 0; - if (len > max_append_len) { - append->_max_len_exceeded = true; - return false; - } - - uint32_t new_len = old_len + len; - BSON_ASSERT(new_len <= max_len); - mcommon_string_grow_to_capacity(string, new_len); - char *buffer = string->str; - - memcpy(buffer + old_len, str, len); - buffer[new_len] = '\0'; - string->len = new_len; - - return mcommon_string_status_from_append(append); -} - -bool -mcommon_string_append_unichar_internal(mcommon_string_append_t *append, bson_unichar_t unichar) -{ - mcommon_string_t *string = append->_string; - uint32_t old_len = string->len; - uint32_t max_len = append->_max_len; - BSON_ASSERT(max_len < UINT32_MAX); - - char max_utf8_sequence[6]; - uint32_t max_append_len = old_len < max_len ? max_len - old_len : 0; - - // Usually we can write the UTF-8 sequence directly - if (BSON_LIKELY(max_append_len >= sizeof max_utf8_sequence)) { - uint32_t actual_sequence_len; - mcommon_string_grow_to_capacity(string, old_len + sizeof max_utf8_sequence); - char *buffer = string->str; - mcommon_utf8_from_unichar(unichar, buffer + old_len, &actual_sequence_len); - BSON_ASSERT(actual_sequence_len <= sizeof max_utf8_sequence); - BSON_ASSERT(append->_max_len_exceeded == false); - uint32_t new_len = old_len + actual_sequence_len; - buffer[new_len] = '\0'; - string->len = new_len; - return true; - } - - // If we are near max_len, avoid growing the buffer beyond it. - uint32_t actual_sequence_len; - mcommon_utf8_from_unichar(unichar, max_utf8_sequence, &actual_sequence_len); - return mcommon_string_append_bytes_internal(append, max_utf8_sequence, actual_sequence_len); -} - -bool -mcommon_string_append_base64_encode(mcommon_string_append_t *append, const uint8_t *bytes, uint32_t len) -{ - BSON_ASSERT_PARAM(append); - BSON_ASSERT_PARAM(bytes); - - if (BSON_UNLIKELY(!mcommon_string_status_from_append(append))) { - return false; - } - - mcommon_string_t *string = append->_string; - uint32_t old_len = string->len; - uint32_t max_len = append->_max_len; - BSON_ASSERT(max_len < UINT32_MAX); - uint32_t max_append_len = old_len < max_len ? max_len - old_len : 0; - - // Note that mcommon_b64_ntop_calculate_target_size includes room for NUL. - // mcommon_b64_ntop includes NUL in the input (buffer size) but not in the return value (string length). - size_t encoded_target_len = mcommon_b64_ntop_calculate_target_size((size_t)len) - 1; - - if (encoded_target_len <= (size_t)max_append_len) { - // No truncation needed. Grow the buffer and encode directly. - mcommon_string_grow_to_capacity(string, old_len + encoded_target_len); - const int tgt = mcommon_b64_ntop(bytes, (size_t)len, string->str + old_len, encoded_target_len + 1); - BSON_ASSERT(mlib_cmp(encoded_target_len, ==, tgt)); - BSON_ASSERT(mlib_in_range(uint32_t, encoded_target_len)); - string->len = old_len + (uint32_t)encoded_target_len; - return true; - } else if (max_append_len == 0) { - // Truncation to a zero-length append - mcommon_string_append_overflow(append); - return false; - } else { - /* We expect to append at least one byte, and truncate. - * Encoding only produces single-byte UTF-8 sequences, so the result always has exactly the maximum length. - * - * mcommon_b64_ntop() can't truncate without failing. To do this without allocating a full size temporary buffer - * or rewriting mcommon_b64_ntop, we can partition the write into three parts: a 'direct' portion made from entire - * non-truncated units of 3 bytes in and 4 characters out, a truncated 'remainder', and an ignored portion. - * Remainders longer than 3 bytes in / 4 bytes out are never necessary, and further portions of the input data - * will not be used. - */ - mcommon_string_grow_to_capacity(string, max_len); - char *buffer = string->str; - - uint32_t remainder_truncated_len = max_append_len % 4; - uint32_t direct_encoded_len = max_append_len - remainder_truncated_len; - uint32_t direct_input_len = mcommon_b64_pton_calculate_target_size((size_t)direct_encoded_len); - BSON_ASSERT(direct_input_len % 3 == 0); - BSON_ASSERT(direct_input_len < len); - const int tgt = mcommon_b64_ntop(bytes, (size_t)direct_input_len, string->str + old_len, direct_encoded_len + 1); - BSON_ASSERT(mlib_cmp(direct_encoded_len, ==, tgt)); - - char remainder_buffer[5]; - uint32_t remainder_input_len = BSON_MIN(3, len - direct_input_len); - BSON_ASSERT(remainder_input_len > 0); - uint32_t remainder_encoded_len = mcommon_b64_ntop_calculate_target_size((size_t)remainder_input_len) - 1; - BSON_ASSERT(remainder_encoded_len > remainder_truncated_len); - const int t2 = mcommon_b64_ntop( - bytes + direct_input_len, (size_t)remainder_input_len, remainder_buffer, sizeof remainder_buffer); - BSON_ASSERT(mlib_cmp(remainder_encoded_len, ==, t2)); - memcpy(buffer + old_len + direct_encoded_len, remainder_buffer, remainder_encoded_len); - - BSON_ASSERT(old_len + direct_encoded_len + remainder_truncated_len == max_len); - buffer[max_len] = '\0'; - string->len = max_len; - mcommon_string_append_overflow(append); - return false; - } -} - -bool -mcommon_string_append_oid_as_hex(mcommon_string_append_t *append, const bson_oid_t *value) -{ - BSON_ASSERT_PARAM(append); - BSON_ASSERT_PARAM(value); - - char oid_str[25]; - bson_oid_to_string(value, oid_str); - return mcommon_string_append(append, oid_str); -} - -bool -mcommon_string_append_selected_chars(mcommon_string_append_t *append, - const char *tmplt, - const char *selector, - size_t selector_len) -{ - BSON_ASSERT_PARAM(append); - BSON_ASSERT_PARAM(tmplt); - BSON_ASSERT_PARAM(selector); - - for (uint8_t template_char; (template_char = (uint8_t)*tmplt); tmplt++) { - BSON_ASSERT(template_char <= 0x7f); - if (memchr(selector, template_char, selector_len) && !mcommon_string_append_unichar(append, template_char)) { - return false; - } - } - return mcommon_string_status_from_append(append); -} - -bool -mcommon_string_append_printf(mcommon_string_append_t *append, const char *format, ...) -{ - BSON_ASSERT_PARAM(append); - BSON_ASSERT_PARAM(format); - - va_list args; - va_start(args, format); - bool ret = mcommon_string_append_vprintf(append, format, args); - va_end(args); - return ret; -} - -bool -mcommon_string_append_vprintf(mcommon_string_append_t *append, const char *format, va_list args) -{ - BSON_ASSERT_PARAM(append); - BSON_ASSERT_PARAM(format); - - if (BSON_UNLIKELY(!mcommon_string_status_from_append(append))) { - return false; - } - - mcommon_string_t *string = append->_string; - uint32_t old_len = string->len; - uint32_t max_len = append->_max_len; - BSON_ASSERT(max_len < UINT32_MAX); - uint32_t max_append_len = old_len < max_len ? max_len - old_len : 0; - - // Initial minimum buffer length; increases on retry. - uint32_t min_format_buffer_capacity = 16; - - while (true) { - // Allocate room for a format buffer at the end of the string. - // It will be at least this round's min_format_buffer_capacity, but if we happen to have extra space allocated we - // do want that to be available to vsnprintf(). - - min_format_buffer_capacity = BSON_MIN(min_format_buffer_capacity, max_append_len); - mcommon_string_grow_to_capacity(string, old_len + min_format_buffer_capacity); - uint32_t alloc = string->alloc; - BSON_ASSERT(alloc > 0 && alloc - 1u >= old_len); - char *format_buffer = string->str + old_len; - uint32_t actual_format_buffer_capacity = BSON_MIN(alloc - 1u - old_len, max_append_len); - BSON_ASSERT(actual_format_buffer_capacity >= min_format_buffer_capacity); - BSON_ASSERT(actual_format_buffer_capacity < UINT32_MAX); - uint32_t format_buffer_alloc = actual_format_buffer_capacity + 1u; - - va_list args_copy; - va_copy(args_copy, args); - int format_result = bson_vsnprintf(format_buffer, format_buffer_alloc, format, args_copy); - va_end(args_copy); - - if (format_result > -1 && mlib_in_range(uint32_t, format_result) && - (uint32_t)format_result <= actual_format_buffer_capacity) { - // Successful result, no truncation. - format_buffer[format_result] = '\0'; - string->len = old_len + (uint32_t)format_result; - BSON_ASSERT(string->len <= append->_max_len); - BSON_ASSERT(append->_max_len_exceeded == false); - return true; - } - - if (actual_format_buffer_capacity == max_append_len) { - // No more space to grow into, this must be the final result. - - if (format_result > -1 && mlib_in_range(uint32_t, format_result) && (uint32_t)format_result < UINT32_MAX) { - // We have truncated output from vsnprintf. Clean it up by removing - // any partial UTF-8 sequences that might be left on the end. - uint32_t truncated_append_len = mcommon_utf8_truncate_len( - format_buffer, BSON_MIN(actual_format_buffer_capacity, (uint32_t)format_result)); - BSON_ASSERT(truncated_append_len <= actual_format_buffer_capacity); - format_buffer[truncated_append_len] = '\0'; - string->len = old_len + truncated_append_len; - append->_max_len_exceeded = true; - return false; - } - - // Error from vsnprintf; This operation fails, but we do not set max_len_exceeded. - return false; - } - - // Choose a larger format_buffer_len and try again. Length will be clamped to max_append_len above. - if (format_result > -1 && mlib_in_range(uint32_t, format_result) && (uint32_t)format_result < UINT32_MAX) { - min_format_buffer_capacity = (uint32_t)format_result + 1u; - } else if (min_format_buffer_capacity < UINT32_MAX / 2) { - min_format_buffer_capacity *= 2; - } else { - min_format_buffer_capacity = UINT32_MAX - 1u; - } - } -} diff --git a/bsonjs/common/common-thread-private.h b/bsonjs/common/common-thread-private.h deleted file mode 100644 index 568be83..0000000 --- a/bsonjs/common/common-thread-private.h +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include - -#include - -#ifndef MONGO_C_DRIVER_COMMON_THREAD_PRIVATE_H -#define MONGO_C_DRIVER_COMMON_THREAD_PRIVATE_H - -#include -#include -#include - -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 - -#define BSON_ONCE_FUN(n) void n(void) -#define BSON_ONCE_RETURN return -#define BSON_ONCE_INIT PTHREAD_ONCE_INIT -#define bson_once(o, c) \ - do { \ - BSON_ASSERT(pthread_once((o), (c)) == 0); \ - } while (0) -#define bson_once_t pthread_once_t -#define bson_thread_t pthread_t -#define BSON_THREAD_FUN(_function_name, _arg_name) void *(_function_name)(void *(_arg_name)) -#define BSON_THREAD_FUN_TYPE(_function_name) void *(*(_function_name))(void *) -#define BSON_THREAD_RETURN return NULL - -/* this macro can be defined as a build configuration option - * with -DENABLE_DEBUG_ASSERTIONS=ON. its purpose is to allow for functions - * that require a mutex to be locked on entry to assert that the mutex - * is actually locked. - * this can prevent bugs where a caller forgets to lock the mutex. */ - -#ifndef MONGOC_ENABLE_DEBUG_ASSERTIONS - -#define bson_mutex_destroy(m) \ - do { \ - BSON_ASSERT(pthread_mutex_destroy((m)) == 0); \ - } while (0) - -#define bson_mutex_init(_n) \ - do { \ - BSON_ASSERT(pthread_mutex_init((_n), NULL) == 0); \ - } while (0) - -#define bson_mutex_lock(m) \ - do { \ - BSON_ASSERT(pthread_mutex_lock((m)) == 0); \ - } while (0) - -#define bson_mutex_t pthread_mutex_t - -#define bson_mutex_unlock(m) \ - do { \ - BSON_ASSERT(pthread_mutex_unlock((m)) == 0); \ - } while (0) - -#else -#include - -typedef struct { - pthread_t lock_owner; - pthread_mutex_t wrapped_mutex; - bool valid_tid; -} bson_mutex_t; - -#define bson_mutex_destroy(mutex) \ - do { \ - BSON_ASSERT(pthread_mutex_destroy(&(mutex)->wrapped_mutex) == 0); \ - } while (0); - -#define bson_mutex_init(mutex) \ - do { \ - BSON_ASSERT(pthread_mutex_init(&(mutex)->wrapped_mutex, NULL) == 0); \ - (mutex)->valid_tid = false; \ - } while (0); - -#define bson_mutex_lock(mutex) \ - do { \ - BSON_ASSERT(pthread_mutex_lock(&(mutex)->wrapped_mutex) == 0); \ - (mutex)->lock_owner = pthread_self(); \ - (mutex)->valid_tid = true; \ - } while (0); - -#define bson_mutex_unlock(mutex) \ - do { \ - (mutex)->valid_tid = false; \ - BSON_ASSERT(pthread_mutex_unlock(&(mutex)->wrapped_mutex) == 0); \ - } while (0); - -#endif - -#else -#include -#define BSON_ONCE_FUN(n) \ - BOOL CALLBACK MLIB_PRAGMA_IF_MSVC(warning(push)) MLIB_PRAGMA_IF_MSVC(warning(disable : 4100)) \ - n(PINIT_ONCE _ignored_a, PVOID _ignored_b, PVOID *_ignored_c) MLIB_PRAGMA_IF_MSVC(warning(pop)) -#define BSON_ONCE_INIT INIT_ONCE_STATIC_INIT -#define BSON_ONCE_RETURN return true -#define bson_mutex_destroy DeleteCriticalSection -#define bson_mutex_init InitializeCriticalSection -#define bson_mutex_lock EnterCriticalSection -#define bson_mutex_t CRITICAL_SECTION -#define bson_mutex_unlock LeaveCriticalSection -#define bson_once(o, c) \ - do { \ - BSON_ASSERT(InitOnceExecuteOnce((o), (c), NULL, NULL)); \ - } while (0) -#define bson_once_t INIT_ONCE -#define bson_thread_t HANDLE -#define BSON_THREAD_FUN(_function_name, _arg_name) unsigned(__stdcall _function_name)(void *(_arg_name)) -#define BSON_THREAD_FUN_TYPE(_function_name) unsigned(__stdcall * _function_name)(void *) -#define BSON_THREAD_RETURN return 0 -#endif - -/* 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 -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) -#define mcommon_mutex_is_locked COMMON_NAME(mutex_is_locked) -bool -mcommon_mutex_is_locked(bson_mutex_t *mutex); -#endif - -/** - * @brief A shared mutex (a read-write lock) - * - * A shared mutex can be locked in 'shared' mode or 'exclusive' mode. Only one - * thread may hold exclusive mode at a time. Any number of threads may hold - * the lock in shared mode simultaneously. No thread can hold in exclusive mode - * while another thread holds in shared mode, and vice-versa. - */ -typedef struct bson_shared_mutex_t { - BSON_IF_WINDOWS(SRWLOCK native;) - BSON_IF_POSIX(pthread_rwlock_t native;) -} bson_shared_mutex_t; - -static BSON_INLINE void -bson_shared_mutex_init(bson_shared_mutex_t *mtx) -{ - BSON_IF_WINDOWS(InitializeSRWLock(&mtx->native)); - BSON_IF_POSIX(BSON_ASSERT(pthread_rwlock_init(&mtx->native, NULL) == 0);) -} - -static BSON_INLINE void -bson_shared_mutex_destroy(bson_shared_mutex_t *mtx) -{ - BSON_IF_WINDOWS((void)mtx;) - BSON_IF_POSIX(BSON_ASSERT(pthread_rwlock_destroy(&mtx->native) == 0);) -} - -static BSON_INLINE void -bson_shared_mutex_lock_shared(bson_shared_mutex_t *mtx) -{ - BSON_IF_WINDOWS(AcquireSRWLockShared(&mtx->native);) - BSON_IF_POSIX(BSON_ASSERT(pthread_rwlock_rdlock(&mtx->native) == 0);) -} - -static BSON_INLINE void -bson_shared_mutex_lock(bson_shared_mutex_t *mtx) -{ - BSON_IF_WINDOWS(AcquireSRWLockExclusive(&mtx->native);) - BSON_IF_POSIX(BSON_ASSERT(pthread_rwlock_wrlock(&mtx->native) == 0);) -} - -static BSON_INLINE void -bson_shared_mutex_unlock(bson_shared_mutex_t *mtx) -{ - BSON_IF_WINDOWS(ReleaseSRWLockExclusive(&mtx->native);) - BSON_IF_POSIX(BSON_ASSERT(pthread_rwlock_unlock(&mtx->native) == 0);) -} - -static BSON_INLINE void -bson_shared_mutex_unlock_shared(bson_shared_mutex_t *mtx) -{ - BSON_IF_WINDOWS(ReleaseSRWLockShared(&mtx->native);) - BSON_IF_POSIX(BSON_ASSERT(pthread_rwlock_unlock(&mtx->native) == 0);) -} - -BSON_END_DECLS - -#endif /* MONGO_C_DRIVER_COMMON_THREAD_PRIVATE_H */ diff --git a/bsonjs/common/common-utf8-private.h b/bsonjs/common/common-utf8-private.h deleted file mode 100644 index 890e8d2..0000000 --- a/bsonjs/common/common-utf8-private.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#ifndef MONGO_C_DRIVER_COMMON_UTF8_PRIVATE_H -#define MONGO_C_DRIVER_COMMON_UTF8_PRIVATE_H - -#include - - -/* - *-------------------------------------------------------------------------- - * - * mcommon_utf8_get_sequence -- - * - * Determine the sequence length of the first UTF-8 character in - * @utf8. The sequence length is stored in @seq_length and the mask - * for the first character is stored in @first_mask. - * - * Returns: - * None. - * - * Side effects: - * @seq_length is set. - * @first_mask is set. - * - *-------------------------------------------------------------------------- - */ - -static BSON_INLINE void -mcommon_utf8_get_sequence(const char *utf8, /* IN */ - uint8_t *seq_length, /* OUT */ - uint8_t *first_mask) /* OUT */ -{ - unsigned char c = *(const unsigned char *)utf8; - uint8_t m; - uint8_t n; - - /* - * See the following[1] for a description of what the given multi-byte - * sequences will be based on the bits set of the first byte. We also need - * to mask the first byte based on that. All subsequent bytes are masked - * against 0x3F. - * - * [1] http://www.joelonsoftware.com/articles/Unicode.html - */ - - if ((c & 0x80) == 0) { - n = 1; - m = 0x7F; - } else if ((c & 0xE0) == 0xC0) { - n = 2; - m = 0x1F; - } else if ((c & 0xF0) == 0xE0) { - n = 3; - m = 0x0F; - } else if ((c & 0xF8) == 0xF0) { - n = 4; - m = 0x07; - } else { - n = 0; - m = 0; - } - - *seq_length = n; - *first_mask = m; -} - - -/* - *-------------------------------------------------------------------------- - * - * mcommon_utf8_from_unichar -- - * - * Converts the unichar to a sequence of utf8 bytes and stores those - * in @utf8. The number of bytes in the sequence are stored in @len. - * - * Parameters: - * @unichar: A bson_unichar_t. - * @utf8: A location for the multi-byte sequence. - * @len: A location for number of bytes stored in @utf8. - * - * Returns: - * None. - * - * Side effects: - * @utf8 is set. - * @len is set. - * - *-------------------------------------------------------------------------- - */ - -static BSON_INLINE void -mcommon_utf8_from_unichar(bson_unichar_t unichar, /* IN */ - char utf8[BSON_ENSURE_ARRAY_PARAM_SIZE(6)], /* OUT */ - uint32_t *len) /* OUT */ -{ - BSON_ASSERT_PARAM(len); - - if (unichar <= 0x7F) { - utf8[0] = unichar; - *len = 1; - } else if (unichar <= 0x7FF) { - *len = 2; - utf8[0] = 0xC0 | ((unichar >> 6) & 0x3F); - utf8[1] = 0x80 | ((unichar) & 0x3F); - } else if (unichar <= 0xFFFF) { - *len = 3; - utf8[0] = 0xE0 | ((unichar >> 12) & 0xF); - utf8[1] = 0x80 | ((unichar >> 6) & 0x3F); - utf8[2] = 0x80 | ((unichar) & 0x3F); - } else if (unichar <= 0x1FFFFF) { - *len = 4; - utf8[0] = 0xF0 | ((unichar >> 18) & 0x7); - utf8[1] = 0x80 | ((unichar >> 12) & 0x3F); - utf8[2] = 0x80 | ((unichar >> 6) & 0x3F); - utf8[3] = 0x80 | ((unichar) & 0x3F); - } else { - *len = 0; - } -} - - -/* - * @brief Calculate a truncation length that preserves UTF-8 validity - * @param str String data, at least 'len' bytes long. - * @returns A new length <= 'len' - * - * When 'str' is a valid UTF-8 string with length >= 'len' bytes, - * this calculates a new length, less than or equal to 'len', which - * guarantees that the string will be truncated in-between code points. - */ - -static BSON_INLINE uint32_t -mcommon_utf8_truncate_len(const char *str, uint32_t len) -{ - uint32_t resulting_len = len; - while (resulting_len > 0) { - if (BSON_LIKELY((uint8_t)str[resulting_len - 1u] <= 0x7f)) { - // Single-byte sequence, always a fine place to stop - return resulting_len; - } - - // Search for the last byte that could begin a UTF-8 sequence - uint32_t seq_begin_at = resulting_len - 1u; - while (((uint8_t)str[seq_begin_at] & 0xc0) == 0x80) { - if (seq_begin_at > 0) { - seq_begin_at--; - } else { - return 0; - } - } - - uint8_t seq_length, first_mask_unused; - mcommon_utf8_get_sequence(str + seq_begin_at, &seq_length, &first_mask_unused); - if (seq_begin_at + seq_length == resulting_len) { - // Sequence is complete, we can truncate here. - return resulting_len; - } - - // Sequence was truncated or invalid; resume search prior to it's beginning. - resulting_len = seq_begin_at; - } - return 0; -} - - -#endif /* MONGO_C_DRIVER_COMMON_UTF8_PRIVATE_H */ diff --git a/bsonjs/common/mlib/ckdint.h b/bsonjs/common/mlib/ckdint.h deleted file mode 100644 index 4e05071..0000000 --- a/bsonjs/common/mlib/ckdint.h +++ /dev/null @@ -1,679 +0,0 @@ -/** - * @file mlib/ckdint.h - * @brief Checked integer arithmetic - * @date 2025-02-04 - * - * This file implements the C23 checked-integer-arithmetic functions as macros. - * - * The function-like macros are defined: - * - * - `mlib_add(Dst, L, R)` / `mlib_add(Dst, A)` - * - `mlib_sub(Dst, L, R)` / `mlib_sub(Dst, A)` - * - `mlib_mul(Dst, L, R)` / `mlib_mul(Dst, A)` - * - `mlib_narrow(Dst, V)` (not from stdckdint, but defined as `mlib_add(Dst, V, 0)`) - * - * Where `Dst` is a pointer to integral storage, and `L` and `R` are arbitrary - * integral expressions. The two-argument variants treat `Dst` as the left-hand - * operand for in-place arithmetic. - * - * Each macro accepts arguments of arbitrary type at any position, and will "do - * the right thing", regardless of the parameter types. No funny integer promotion, - * sign extension, sign conversion, nor implicit narrowing. The macros return `false` - * if-and-only-if the result was lossless. They return `true` if-and-only-if the - * value written to `Dst` does not represent the true arithmetic result. - * - * The following additional macros are defined: - * - * - `mlib_assert_add(T, L, R)` - * - `mlib_assert_sub(T, L, R)` - * - `mlib_assert_mul(T, L, R)` - * - * Where `T` is an integer type. The macro will yield a value of that type, asserting - * that the operation on `L` and `R` does not overflow. If the operation overflows, - * the program will be terminated with a diagnostic to `stderr` pointing to the call site. - * - * For implementation details and a usage guide, see `ckdint.md` - * - * @copyright Copyright (c) 2025 - * - * 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. - */ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -mlib_extern_c_begin(); - -/** - * @brief Perform integer addition - * - * @param Out A non-null pointer to a modifiable integer. - * @param A The left-hand addend of addition - * @param B The right-hand addend of addition - * @retval `true` if the value written to `Out` does not represent the true arithmetic sum. - * @retval `false` Otherwise. - * - * The sum `A + B` is written to `Out`. The operation is commutative. - * - * If the argument `B` is omitted, computes `*Out + A` (performs in-place addition). - */ -#define mlib_add(...) MLIB_ARGC_PICK(_mlib_ckdint, mlib_add, __VA_ARGS__) -/** - * @brief Perform integer subtraction - * - * @param Out A non-null pointer to a modifiable integer. - * @param A The left-hand operand of the subtraction (minuend) - * @param B The right-hand operand of subtraction (subtrahend) - * @retval `true` if the value written to `Out` does not represent the true arithmetic difference. - * @retval `false` Otherwise. - * - * The difference `A - B` will be written to `Out`. - * - * If the argument `B` is omitted, computes `*Out - A` (performs in-place subtraction) - */ -#define mlib_sub(...) MLIB_ARGC_PICK(_mlib_ckdint, mlib_sub, __VA_ARGS__) -/** - * @brief Perform integer multiplication - * - * @param Out A non-null pointer to a modifiable integer. - * @param A The left-hand factor of multiplication - * @param B The right-hand factor of multiplication - * @retval `true` if the value written to `Out` does not represent the true arithmetic product. - * @retval `false` Otherwise. - * - * The product `A × B` will be written to `Out`. The operation is commutative. - * - * If the argument `B` is omitted, computes `Out × A` (performs in-place multiplication) - */ -#define mlib_mul(...) MLIB_ARGC_PICK(_mlib_ckdint, mlib_mul, __VA_ARGS__) -/** - * @brief Perform narrowing assignment from one integer value to another. - * - * @param Out A non-null pointer to a modifiable integer. - * @param A The integer value to be narrowed. - * @retval `true` if the value written to `Out` is not equivalent to the value of `A` - * @retval `false` otherwise - */ -#define mlib_narrow(O, A) mlib_add((O), (A), 0) - -/** - * @brief Perform an asserting addition, yielding the result - * - * @param T The target type of the operation - * @param A The left-hand addend for the addition - * @param B The right-hand addend for the addition - * @return The sum `A + B` as type `T` - * - * If the true arithmetic sum is not representable in `T`, the program terminates. - */ -#define mlib_assert_add(T, A, B) \ - _mlib_assert_ckdint(T, A, B, &mlib_add, "mlib_assert_add", #T, #A, #B, mlib_this_source_location()) -/** - * @brief Perform an asserting subtraction, yielding the result - * - * @param T The target type of the operation - * @param A The left-hand minuend for the subtraction - * @param B The right-hand subtrahend for the subtraction - * @return The difference `A - B` as type `T` - * - * If the true arithmetic difference is not representable in `T`, the program terminates. - */ -#define mlib_assert_sub(T, A, B) \ - _mlib_assert_ckdint(T, A, B, &mlib_sub, "mlib_assert_sub", #T, #A, #B, mlib_this_source_location()) -/** - * @brief Perform an asserting multiplication, yielding the result - * - * @param T The target type of the operation - * @param A The left-hand factor for the multiplication - * @param B The right-hand factor for the multiplication - * @return The product `A × B` as type `T` - * - * If the true arithmetic product is not representable in `T`, the program terminates. - */ -#define mlib_assert_mul(T, A, B) \ - _mlib_assert_ckdint(T, A, B, &mlib_mul, "mlib_assert_mul", #T, #A, #B, mlib_this_source_location()) - -/** - * @brief Perform a runtime-checked cast of an integral value to another type. - * - * @param T A type specifier for a target integral type for the cast. - * @param Operand The integral value to be converted. - * - * If the cast would result in the operand value changing, the program will be - * terminated with a diagnostic. - */ -#define mlib_assert_narrow(T, Operand) \ - (T) _mlib_checked_cast( \ - mlib_minof(T), mlib_maxof(T), mlib_upsize_integer(Operand), #T, #Operand, mlib_this_source_location()) - -#define _mlib_ckdint_argc_3(Fn, Out, Arg) _mlib_ckdint_argc_4(Fn, Out, *(Out), Arg) -#define _mlib_ckdint_argc_4(Fn, O, A, B) \ - _mlib_ckdint(O, \ - sizeof(*(O)), \ - _mlibClobberIsSigned(*(O), 0) ? (intmax_t)_mlibMinofSigned(*(O)) : _mlibMinofUnsigned(*(O)), \ - _mlibClobberIsSigned(*(O), 1) ? _mlibMaxofSigned(*(O)) : _mlibMaxofUnsigned(*(O)), \ - mlib_upsize_integer(A), \ - mlib_upsize_integer(B), \ - &Fn) - -// Impl macro for the asserting checked arithmetic functions -#define _mlib_assert_ckdint(T, A, B, Fn, F_str, T_str, A_str, B_str, Here) \ - ((T)_mlib_assert_ckdint(sizeof(T), \ - mlib_minof(T), \ - mlib_maxof(T), \ - mlib_upsize_integer(A), \ - mlib_upsize_integer(B), \ - Fn, \ - F_str, \ - T_str, \ - A_str, \ - B_str, \ - Here)) - -// clang-format off -// Generates an 0b11111 bit pattern for appropriate size: -#define _mlibMaxofUnsigned(V) \ - /* NOLINTNEXTLINE(bugprone-sizeof-expression) */ \ - mlib_bits(mlib_bitsizeof((V)), 0) - -// Generates an 0b01111 bit pattern for the two's complement max value: -#define _mlibMaxofSigned(V) \ - /* NOLINTNEXTLINE(bugprone-sizeof-expression) */ \ - mlib_bits(mlib_bitsizeof(V) - 1u, 0) -// Generates an 0b10000... bit pattern for the two's complement min value: -#define _mlibMinofSigned(V) \ - /* NOLINTNEXTLINE(bugprone-sizeof-expression) */ \ - (0 - mlib_bits(1, mlib_bitsizeof(V) - 1u)) -// For completeness: -#define _mlibMinofUnsigned(V) 0 -// Yields true iff the operand expression has a signed type, but requires that -// the operand is a modifiable l-value. The `N` must be 0 or 1, arbitrarily (see below). -#define _mlibClobberIsSigned(V, N) \ - MLIB_IF_ELSE(mlib_have_typeof()) \ - /* Prefer using typeof(), if we can. */ \ - (mlib_is_signed(mlib_typeof(V))) \ - /* Otherwise, do a dance: */ \ - (( \ - /* Save the value of V */ \ - _mlibSignCheckTmp[N] = 0ull | (uintmax_t) (V), \ - /* Set V to zero, and check whether decrementing results in a negative value */ \ - (V) = 0, \ - _mlibSignCheckResult[N] = (--(V) < 0), \ - /* Restore the value of V (bit hacks to prevent conversion warnings) */ \ - (V) = 0, \ - (V) |= _mlibSignCheckTmp[N], \ - /* Yield the sign-check result */ \ - _mlibSignCheckResult[N] \ - )) -// Storage for `_mlibClobberIsSigned`. We use more than one storage space to prevent -// unsequenced-operation warnings when we use `_mlibClobberIsSigned` multiple times -// in a function argument list. GCC and Clang are able to easily elide these from -// an optimized TU. MSVC has trouble, but is still able to constant-fold where it matters. -static mlib_maybe_unused mlib_thread_local uintmax_t _mlibSignCheckTmp[2]; -static mlib_maybe_unused mlib_thread_local bool _mlibSignCheckResult[2]; -// clang-format on - -// Compile-time assert that the compiler's integer conversions obey two's complement encoding -mlib_static_assert((intmax_t)UINTMAX_MAX == -1 // - && (intmax_t)(UINTMAX_MAX - 5) == -6, - "This file requires two's complement signed integers"); - -/** - * @brief Function signature for checked arithmetic support functions - * - * The function operates on max-precision integers of either sign, and should - * return true iff the arithmetic operation overflows for the given sign configuration. - * - * @param dst The output parameter for the operation. Never a null pointer. - * @param dst_signed Whether to treat the result as a signed integer - * @param a_signed Whether to treat the `a` operand as signed - * @param a The left-hand operand of the operation - * @param b_signed Whether to treat the `b` operand as signed - * @param b The right-hand operand of the operation - * - * @note This was original written to use `mlib_upscaled_integer` for `a/b/dst`, but - * this defeats MSVC's ability to inline an indirect call through a constant-folded function - * pointer with this signature. GCC and Clang handle this fine, but for MSVC performance - * the more verbose signature is used. - */ -typedef bool (*_mlib_ckdint_arith_fn)( - uintmax_t *dst, bool dst_signed, bool a_signed, uintmax_t a, bool b_signed, uintmax_t b); - -// Support function for the `mlib_add` macro -static inline bool(mlib_add)(uintmax_t *dst, bool dst_signed, bool a_signed, uintmax_t a, bool b_signed, uintmax_t b) - mlib_noexcept -{ - // Perform regular wrapping arithmetic on the unsigned value. The bit pattern - // is equivalent if there is two's complement signed arithmetic. - const uintmax_t sum = *dst = a + b; - const uintmax_t signbit = mlib_bits(1, mlib_bitsizeof(uintmax_t) - 1u); - // Now we check whether that overflowed according to the sign configuration. - // We use some bit fiddling magic that treat the signbit as a boolean for - // "is this number negative?" or "is this number “large” (i.e. bigger than signed-max)?" - // The expanded verbose form of each bit-magic is written below the more esoteric cases - if (dst_signed) { - if (a_signed) { - if (b_signed) { // S = S + S - return signbit & (sum ^ a) & (sum ^ b); - // Expanded: - // Test whether the product sign is unequal to both input signs - // X ^ Y yields a negative value if the signs are unequal - // const bool a_signflipped = (intmax_t) (sum ^ a) < 0; - // const bool b_signflipped = (intmax_t) (sum ^ b) < 0; - // return a_signflipped && b_signflipped; - } else { // S = S + U - // Flip the sign bit of a, test whether that sum overflows - a ^= signbit; - return a + b < a; - } - } else { - if (b_signed) { // S = U + S - // Flip the sign bit of `b`, test whether that sum overflows - b ^= signbit; - return a + b < b; - } else { // S = U + U - // The signed sum must not be less than the addend, and not negative - return sum < a || (signbit & sum); - } - } - } else { - if (a_signed) { - if (b_signed) { // U = S + S - return signbit & (((sum | a) & b) | ((sum & a) & ~b)); - // Expanded: - // const bool a_is_negative = (intmax_t) a < 0; - // const bool b_is_negative = (intmax_t) b < 0; - // const bool sum_is_large = sum > INTMAX_MAX; - // if (b_is_negative) { - // if (a_is_negative) { - // // The sum must be negative, and therefore cannot be stored in an unsigned - // return true; - // } else if (sum_is_large) { - // // We added a negative value B to a positive value A, but the sum - // // ended up larger than the max signed value, so we wrapped - // return true; - // } - // } else if (a_is_negative) { - // if (sum_is_large) { - // // Same as above case with sum_is_large - // return true; - // } - // } - // return false; - } else { // U = S + U - return signbit & (sum ^ a ^ signbit) & (sum ^ b); - // Expanded: - // const bool sum_is_large = sum > INTMAX_MAX; - // const bool b_is_large = b > INTMAX_MAX; - // const bool a_is_negative = (intmax_t) a < 0; - // if (!a_is_negative && b_is_large) { - // // We are adding a non-negative value to a large number, so the - // // sum must also be large - // if (!sum_is_large) { - // // We ended up with a smaller value, meaning that we must have wrapped - // return true; - // } - // } - // if (a_is_negative && !b_is_large) { - // // We subtracted a non-negative value from a non-large number, so - // // the result should not be large - // if (sum_is_large) { - // // We ended up with a large value, so we must have wrapped - // return true; - // } - // } - // return false; - } - } else { - if (b_signed) { // U = U + S --- (See [U = S + U] for an explanation) - return signbit & (sum ^ a) & (sum ^ b ^ signbit); - } else { // U = U + U (simple case) - return sum < a; - } - } - } -} - -// Support for the `mlib_sub` macro -static inline bool(mlib_sub)(uintmax_t *dst, bool dst_signed, bool a_signed, uintmax_t a, bool b_signed, uintmax_t b) - mlib_noexcept -{ - // Perform the subtraction using regular wrapping arithmetic - const uintmax_t diff = *dst = a - b; - const uintmax_t signbit = mlib_bits(1, mlib_bitsizeof(uintmax_t) - 1u); - // Test whether the operation overflowed for the given sign configuration - // (See mlib_add for more details on why we do this bit fiddling) - if (dst_signed) { - const bool diff_is_negative = signbit & diff; - if (a_signed) { - if (b_signed) { // S = S - S - return signbit & (a ^ b) & (diff ^ a); - // Explain: - // const bool a_is_negative = (intmax_t) a < 0; - // const bool b_is_negative = (intmax_t) b < 0; - // if (a_is_negative != b_is_negative) { - // // Given: Pos - Neg = Pos - // // ∧ Neg - Pos = Neg - // // We expect that the difference preserves the sign of the minuend - // if (diff_is_negative != a_is_negative) { - // return true; - // } - // } - // // Otherwise, `Pos - Pos` and `Neg - Neg` cannot possibly overflow - // return false; - } else { // S = S - U - // The diff overflows if the sign-bit-flipped minuend is smaller than the subtrahend - return (a ^ signbit) < b; - } - } else { - if (b_signed) { // S = U - S - // The diff overflows if the sign-bit-flipped subtrahend is greater than or equal to the minuend - return a >= (b ^ signbit); - } else { // S = U - U - const bool expect_negative = a < b; - return expect_negative != diff_is_negative; - } - } - } else { - if (a_signed) { - if (b_signed) { // U = S - S - return signbit & (((diff & a) & b) | ((diff | a) & ~b)); - // Expanded: - // const bool a_is_negative = (intmax_t) a < 0; - // const bool b_is_negative = (intmax_t) b < 0; - // const bool diff_is_large = diff > INTMAX_MAX; - // if (!b_is_negative) { - // if (a_is_negative) { - // // We subtracted a non-negative from a negative value, so the difference - // // must be negative and cannot be stored as unsigned - // return true; - // } - // if (diff_is_large) { - // // We subtracted a positive value from a signed value, so we must not - // // end up with a large value - // return true; - // } - // } - // if (a_is_negative) { - // if (diff_is_large) { - // // A is negative, and there is no possible value that we can subtract - // // from it to obtain this large integer, so we must have overflowed - // return true; - // } - // } - // return false; - } else { // - return (b > a) || (signbit & a); - } - } else { - if (b_signed) { // U = U - S - return signbit & (a ^ b ^ signbit) & (diff ^ a); - // Explain: - // const bool a_is_large = a > INTMAX_MAX; - // const bool b_is_negative = (intmax_t) b < 0; - // const bool diff_is_large = diff > INTMAX_MAX; - // if (a_is_large && b_is_negative) { - // // The difference between a large value and a negative - // // value must also be a large value - // if (!diff_is_large) { - // // We expected another large value to appear. - // return true; - // } - // } - // if (!a_is_large && !b_is_negative) { - // // The difference between a non-large positive value and a non-negative value - // // must not be a large value - // if (diff_is_large) { - // // We did not expect a large difference - // return true; - // } - // } - // return false; - } else { - return a < b; - } - } - } -} - -// Support for the `mlib_mul` macro -static inline bool(mlib_mul)(uintmax_t *dst, bool dst_signed, bool a_signed, uintmax_t a, bool b_signed, uintmax_t b) - mlib_noexcept -{ - // Multiplication is a lot more subtle - const uintmax_t signbit = mlib_bits(1, mlib_bitsizeof(uintmax_t) - 1u); - if (dst_signed) { - if (a_signed) { - if (b_signed) { - // S = S × S - *dst = a * b; - if (((intmax_t)b == -1 && (intmax_t)a == INTMAX_MIN) || ((intmax_t)a == -1 && (intmax_t)b == INTMAX_MIN)) { - // MIN × -1 is undefined - return true; - } - if (a && (intmax_t)*dst / (intmax_t)a != (intmax_t)b) { - // Mult did not preserve the arithmetic identity - return true; - } - return false; - } else { - // S = S × U - *dst = a * b; - const bool a_is_negative = signbit & a; - const uintmax_t positive_a = a_is_negative ? (0 - a) : a; - const uintmax_t positive_prod = positive_a * b; - const bool did_overflow = positive_a && positive_prod / positive_a != b; - if (did_overflow) { - return true; - } - if (positive_prod > (uintmax_t)INTMAX_MAX + (unsigned)a_is_negative) { - return true; - } - return false; - } - } else { - if (b_signed) { - // S = U × S - // Swap args: [S = S × U] - return (mlib_mul)(dst, dst_signed, b_signed, b, a_signed, a); - } else { - // S = U × U - *dst = a * b; - const bool did_overflow = a && *dst / a != b; - if (did_overflow) { - return true; - } - if (signbit & *dst) { - // A negative product indicates wrapping - return true; - } - return false; - } - } - } else { - if (a_signed) { - if (b_signed) { - // U = S × S - // Is either operand the min? - bool either_min = false; - if (signbit & a & b) { - // Both negative: Flip the signs - a = 0 - a; - b = 0 - b; - // MIN is pathological: 0 - MIN = MIN, so we need to check that: - either_min = (intmax_t)a == INTMAX_MIN || (intmax_t)b == INTMAX_MIN; - } - // Check if the product would be a negative number - const bool neg_prod = (signbit & (a ^ b)) && a && b && !either_min; - *dst = a * b; - return neg_prod || (a && *dst / a != b); - } else { - // U = S × U - *dst = a * b; - const bool did_ovr = a && *dst / a != b; - const bool a_is_negative = signbit & a; - if (did_ovr || (a_is_negative && b)) { - return true; - } - return false; - } - } else { - if (b_signed) { - // U = U × S - // Swap to [U = S × U] - return (mlib_mul)(dst, dst_signed, b_signed, b, a_signed, a); - } else { - // U = U × U: Simple: - *dst = a * b; - return a && *dst / a != b; - } - } - } -} - -/** - * @private - * @brief This function performs the narrowing checks around a ckdint funciton - * - * @param dst Pointer to the target interger - * @param dst_sz The size of the target integer, in bytes - * @param minval The minimum value for the result. If negative, the target is treated as signed - * @param maxval The maximum value for the result - * @param a The left-hand operand for the operation - * @param b The right-hand operand for the operation - * @param fn The arithmetic function that performs arithmetic on the max-precision integer - * @return true If the resulting value DOES NOT equal the true arithmetic result - * @return false If the resulting value represents the true arithmetic results - */ -static inline bool -_mlib_ckdint(void *dst, - int dst_sz, - intmax_t minval, - uintmax_t maxval, - struct mlib_upsized_integer a, - struct mlib_upsized_integer b, - _mlib_ckdint_arith_fn fn) mlib_noexcept -{ - // Perform the arithmetic on uintmax_t, for wrapping behavior - uintmax_t tmp; - bool ovr = fn(&tmp, minval < 0, a.is_signed, a.bits.as_unsigned, b.is_signed, b.bits.as_unsigned); - // Endian-adjusting for writing the result - const char *copy_from = (const char *)&tmp; - if (!mlib_is_little_endian()) { - // We need to adjust the copy src in order to truncate the integer for big-endian encoding. - // Number of high bytes that we need to drop: - const int n_drop = (int)sizeof(tmp) - dst_sz; - // Adjust the copy pointer to so that we copy from the most significant byte that - // we wish to keep - copy_from += n_drop; - } else { - // For little-endian native, we don't need to adjust the bytes, since we can just - // truncate using the memcpy() - } - // Send the result to the destination - memcpy(dst, copy_from, (size_t)dst_sz); - // Final range check: - if (minval < 0) { - // Treat the target as signed: - intmax_t idst = (intmax_t)tmp; - return ovr || idst < minval || (idst > 0 && (uintmax_t)idst > maxval); - } else { - return ovr || tmp > maxval; - } -} - -/** - * @internal - * @brief Implementation function for the asserting arithmetic functions - */ -static inline uintmax_t(_mlib_assert_ckdint)(size_t dst_sz, - intmax_t minval, - uintmax_t maxval, - struct mlib_upsized_integer a, - struct mlib_upsized_integer b, - _mlib_ckdint_arith_fn arith, - const char *fn_str, - const char *type_str, - const char *a_str, - const char *b_str, - struct mlib_source_location here) mlib_noexcept -{ - uintmax_t tmp; - bool did_overflow = _mlib_ckdint(&tmp, dst_sz, minval, maxval, a, b, arith); - if (did_overflow) { - fprintf(stderr, - "%s:%d: [in %s]: Call of %s(%s, %s, %s) resulted in arithmetic overflow\n", - here.file, - here.lineno, - here.func, - fn_str, - type_str, - a_str, - b_str); - abort(); - } - if (!mlib_is_little_endian()) { - // We unconditionally set the leading bytes of `tmp`, but big-endian expects - // the lower place values to be in the later bytes. If the target int is - // smaller than intmax, we must shift all the bits over to their proper - // position. This expression is trivially constant-folded by an optimizer. - tmp >>= ((size_t)CHAR_BIT * ((sizeof tmp) - dst_sz)); - } - return tmp; -} - -static inline uintmax_t -_mlib_checked_cast(intmax_t min_, - uintmax_t max_, - struct mlib_upsized_integer val, - const char *typename_, - const char *expr, - struct mlib_source_location here) mlib_noexcept -{ - if (!(mlib_in_range)(min_, max_, val)) { - if (val.is_signed) { - fprintf(stderr, - "%s:%d: in [%s]: Checked integer cast of “%s” (value = %lld) to “%s” loses information\n", - here.file, - here.lineno, - here.func, - expr, - (long long)val.bits.as_signed, - typename_); - } else { - fprintf(stderr, - "%s:%d: in [%s]: Checked integer cast of “%s” (value = %llu) to “%s” loses information\n", - here.file, - here.lineno, - here.func, - expr, - (unsigned long long)val.bits.as_unsigned, - typename_); - } - fflush(stderr); - abort(); - } - if (val.is_signed) { - return (uintmax_t)val.bits.as_signed; - } - return val.bits.as_unsigned; -} - -mlib_extern_c_end(); diff --git a/bsonjs/common/mlib/cmp.h b/bsonjs/common/mlib/cmp.h deleted file mode 100644 index 8a674ff..0000000 --- a/bsonjs/common/mlib/cmp.h +++ /dev/null @@ -1,132 +0,0 @@ -/** - * @file mlib/cmp.h - * @brief Safe integer comparison and range checking - * @date 2024-08-29 - * - * This file provides safe and intuitive integer comparison macros that behave - * appropriately, regardless of the sign or precision of the integer operands. - * - * @copyright Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MLIB_CMP_H_INCLUDED -#define MLIB_CMP_H_INCLUDED - -#include -#include - -#include -#include - -/** - * @brief Result type of comparing two integral values with `mlib_cmp` - * - * The enumerator values are chosen such that they can be compared with zero - */ -enum mlib_cmp_result { - // The two values are equivalent - mlib_equal = 0, - // The left-hand operand is less than the right-hand - mlib_less = -1, - // The left-hand operand is greater than the right-hand - mlib_greater = 1, -}; - -/** - * @brief Compare two integral values safely. - * - * This function can be called with two arguments or with three: - * - * - `mlib_cmp(a, b)` Returns a value of type `mlib_cmp_result` - * - `mlib_cmp(a, Op, b)` where `Op` is a relational operator. Evaluates to a boolean value. - */ -#define mlib_cmp(...) MLIB_ARGC_PICK(_mlib_cmp, __VA_ARGS__) -// Compare two integers, and return the result of that comparison: -#define _mlib_cmp_argc_2(L, R) mlib_cmp(mlib_upsize_integer((L)), mlib_upsize_integer((R)), 0) -// Compare two integers, but with an infix operator: -#define _mlib_cmp_argc_3(L, Op, R) (mlib_cmp(mlib_upsize_integer((L)), mlib_upsize_integer((R)), 0) Op 0) -// Impl for mlib_cmp -mlib_always_inline static enum mlib_cmp_result(mlib_cmp)(struct mlib_upsized_integer x, - struct mlib_upsized_integer y, - int always_zero) mlib_noexcept -{ - (void)always_zero; -#if mlib_is_optimized_build() && !mlib_is_msvc() && \ - !(defined(MLIB_DISABLE_INLINING_ASSERTIONS) && MLIB_DISABLE_INLINING_ASSERTIONS) - if (always_zero != 0) { - // All calls must pass zero for `always_zero`. In optimized builds, we assert that this function - // is always inlined, and that dead-code-elim triggers to delete this branch that calls - // a never-defined function. - extern void __assert_that_mlib_cmp_was_inlined(void); - // If you see a call to this function ↑ produce a link error here ↓, it means - // that inlining or DCE failed within the relevant translation unit. - __assert_that_mlib_cmp_was_inlined(); - } -#endif - if (x.is_signed) { - if (y.is_signed) { - // Both signed - if (x.bits.as_signed < y.bits.as_signed) { - return mlib_less; - } else if (x.bits.as_signed > y.bits.as_signed) { - return mlib_greater; - } - } else { - // X signed, Y unsigned - if (x.bits.as_signed < 0 || (uintmax_t)x.bits.as_signed < y.bits.as_unsigned) { - return mlib_less; - } else if ((uintmax_t)x.bits.as_signed > y.bits.as_unsigned) { - return mlib_greater; - } - } - } else { - if (!y.is_signed) { - // Both unsigned - if (x.bits.as_unsigned < y.bits.as_unsigned) { - return mlib_less; - } else if (x.bits.as_unsigned > y.bits.as_unsigned) { - return mlib_greater; - } - } else { - // X unsigned, Y signed - if (y.bits.as_signed < 0 || x.bits.as_unsigned > (uintmax_t)y.bits.as_signed) { - return mlib_greater; - } else if (x.bits.as_unsigned < (uintmax_t)y.bits.as_signed) { - return mlib_less; - } - } - } - return mlib_equal; -} - -/** - * @brief Test whether the given operand is within the range of some other integral type - * - * @param T A type specifier of the target integral type - * @param Operand the expression that is being inspected. - * - * @note This macro may evaluate the operand more than once - */ -#define mlib_in_range(T, Operand) \ - mlib_in_range((intmax_t)mlib_minof(T), (uintmax_t)mlib_maxof(T), mlib_upsize_integer(Operand)) -static inline bool(mlib_in_range)(intmax_t min_, uintmax_t max_, struct mlib_upsized_integer val) mlib_noexcept -{ - if (val.is_signed) { - return mlib_cmp(val.bits.as_signed, >=, min_) && mlib_cmp(val.bits.as_signed, <=, max_); - } else { - return mlib_cmp(val.bits.as_unsigned, >=, min_) && mlib_cmp(val.bits.as_unsigned, <=, max_); - } -} - -#endif // MLIB_CMP_H_INCLUDED diff --git a/bsonjs/common/mlib/config.h b/bsonjs/common/mlib/config.h deleted file mode 100644 index a95f917..0000000 --- a/bsonjs/common/mlib/config.h +++ /dev/null @@ -1,419 +0,0 @@ -/** - * @file mlib/config.h - * @brief Provides utility macros - * @date 2024-08-29 - * - * @note This file is intented to be standalone-includable, with no dependencies - * other than the standard library and platform headers. This file (and other - * `mlib` files), are for internal use only, and should not be used in any public - * headers. - * - * @copyright Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MLIB_CONFIG_H_INCLUDED -#define MLIB_CONFIG_H_INCLUDED - -#ifndef _WIN32 -#include // Endian detection -#endif - -/** - * @brief A function-like macro that always expands to nothing - */ -#define MLIB_NOTHING(...) - -/** - * @brief A function macro that simply expands to its arguments unchanged - */ -#define MLIB_JUST(...) __VA_ARGS__ - -// Paste two tokens -#ifndef _MSC_VER -#define MLIB_PASTE(A, ...) _mlibPaste1(A, __VA_ARGS__) -#else -#define MLIB_PASTE(A, ...) MLIB_JUST(_mlibPaste1(A, __VA_ARGS__)) -#endif -// Paste three tokens -#define MLIB_PASTE_3(A, B, ...) MLIB_PASTE(A, MLIB_PASTE(B, __VA_ARGS__)) -// Paste four tokens -#define MLIB_PASTE_4(A, B, C, ...) MLIB_PASTE(A, MLIB_PASTE_3(B, C, __VA_ARGS__)) -// Paste five tokens -#define MLIB_PASTE_5(A, B, C, D, ...) MLIB_PASTE(A, MLIB_PASTE_4(B, C, D, __VA_ARGS__)) -#define _mlibPaste1(A, ...) A##__VA_ARGS__ - -/** - * @brief Convert the token sequence into a string after macro expansion - */ -#define MLIB_STR(...) _mlibStr(__VA_ARGS__) -#define _mlibStr(...) #__VA_ARGS__ - -#define MLIB_EVAL_32(...) MLIB_EVAL_16(MLIB_EVAL_16(__VA_ARGS__)) -#define MLIB_EVAL_16(...) MLIB_EVAL_8(MLIB_EVAL_8(__VA_ARGS__)) -#define MLIB_EVAL_8(...) MLIB_EVAL_4(MLIB_EVAL_4(__VA_ARGS__)) -#define MLIB_EVAL_4(...) MLIB_EVAL_2(MLIB_EVAL_2(__VA_ARGS__)) -#define MLIB_EVAL_2(...) MLIB_EVAL_1(MLIB_EVAL_1(__VA_ARGS__)) -#define MLIB_EVAL_1(...) __VA_ARGS__ - -// clang-format off -/** - * @brief Expand to 1 if given no arguments, otherwise 0. - * - * This could be done trivially using __VA_OPT__, but we need to work on - * older compilers. - */ -#define MLIB_IS_EMPTY(...) \ - _mlibIsEmpty_1( \ - /* Expands to '1' if __VA_ARGS__ contains any top-level commas */ \ - _mlibHasComma(__VA_ARGS__), \ - /* Expands to '1' if __VA_ARGS__ begins with a parenthesis, because \ - * that will cause an "invocation" of _mlibCommaIfParens, \ - * which immediately expands to a single comma. */ \ - _mlibHasComma(_mlibCommaIfParens __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 */ \ - _mlibHasComma(__VA_ARGS__ ()), \ - /* Expands to '1' if __VA_ARGS__ expands to nothing. */ \ - _mlibHasComma(_mlibCommaIfParens __VA_ARGS__ ())) -// Expand to 1 if the argument list has a comma. The weird definition is to support -// old MSVC's bad preprocessor -#define _mlibHasComma(...) \ - MLIB_JUST(_mlibPickSixteenth \ - MLIB_NOTHING("MSVC workaround") \ - (__VA_ARGS__, 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 -#define _mlibCommaIfParens(...) , - -/** - * @brief Expands to `1` if the given macro argument is a parenthesized group of - * tokens, otherwise `0` - */ -#define MLIB_IS_PARENTHESIZED(X) \ - _mlibHasComma(_mlibCommaIfParens X) - -/** - * @brief Pass a function-like macro name, inhibiting its expansion until the - * next pass: - * - * #define func_macro(x) x - * - * MLIB_DEFERRED(func_macro)(foo) // Expands to "func_macro(foo)", not "foo" - */ -#define MLIB_DEFERRED(MacroName) \ - /* Expand to the macro name: */ \ - MacroName \ - /*- - * Place a separator between the function macro name and whatever comes next - * in the file. Presumably, the next token will be the parens to invoke "MacroName", - * but this separator inhibits its expansion unless something else comes - * along to do another expansion pass - */ \ - MLIB_NOTHING("[separator]") - -/** - * A helper for isEmpty(): If given (0, 0, 0, 1), expands as: - * - first: _mlibHasComma(_mlibIsEmptyCase_0001) - * - then: _mlibHasComma(,) - * - then: 1 - * Given any other aruments: - * - first: _mlibHasComma(_mlibIsEmptyCase_) - * - then: 0 - */ -#define _mlibIsEmpty_1(_1, _2, _3, _4) \ - _mlibHasComma(MLIB_PASTE_5(_mlibIsEmptyCase_, _1, _2, _3, _4)) -#define _mlibIsEmptyCase_0001 , - -#define MLIB_IS_NOT_EMPTY(...) MLIB_PASTE (_mlibNotEmpty_, MLIB_IS_EMPTY (__VA_ARGS__)) -#define _mlibNotEmpty_1 0 -#define _mlibNotEmpty_0 1 -// clang-format on - -/** - * @brief If the argument expands to `0`, `false`, or nothing, expands to `0`. - * Otherwise expands to `1`. - */ -#define MLIB_BOOLEAN(...) MLIB_IS_NOT_EMPTY(MLIB_PASTE_3(_mlib, Bool_, __VA_ARGS__)) -#define _mlibBool_0 -#define _mlibBool_false -#define _mlibBool_ - -/** - * @brief A ternary macro. Expects three parenthesized argument lists in - * sequence. - * - * If the first argument list is a truthy value, expands to the second argument - * list. Otherwise, expands to the third argument list. The unused argument list - * is not expanded and is discarded. - */ -#define MLIB_IF_ELSE(...) MLIB_PASTE(_mlibIfElseBranch_, MLIB_BOOLEAN(__VA_ARGS__)) -#define _mlibIfElseBranch_1(...) __VA_ARGS__ MLIB_NOTHING -#define _mlibIfElseBranch_0(...) MLIB_JUST - -/** - * @brief Expands to an integer literal corresponding to the number of macro - * arguments. Supports up to fifteen arguments. - */ -#define MLIB_ARG_COUNT(...) \ - MLIB_IF_ELSE(MLIB_IS_EMPTY(__VA_ARGS__)) \ - (0)(_mlibPickSixteenth(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) -#define _mlibPickSixteenth(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, ...) _16 - -/** - * @brief Expand to a call expression `Prefix##_argc_N(...)`, where `N` is the - * number of macro arguments. - * - * XXX: The `MLIB_JUST` forces an additional expansion pass that works around a - * bug in the old MSVC preprocessor, but is not required in a conforming preprocessor. - */ -#define MLIB_ARGC_PICK(Prefix, ...) MLIB_JUST(MLIB_ARGC_PASTE(Prefix, __VA_ARGS__)(__VA_ARGS__)) -#define MLIB_ARGC_PASTE(Prefix, ...) MLIB_PASTE_3(Prefix, _argc_, MLIB_ARG_COUNT(__VA_ARGS__)) - -#ifdef __cplusplus -#define mlib_is_cxx() 1 -#define mlib_is_not_cxx() 0 -#define MLIB_IF_CXX(...) __VA_ARGS__ -#define MLIB_IF_NOT_CXX(...) -#else -#define mlib_is_cxx() 0 -#define mlib_is_not_cxx() 1 -#define MLIB_IF_CXX(...) -#define MLIB_IF_NOT_CXX(...) __VA_ARGS__ -#endif - -#define MLIB_LANG_PICK MLIB_IF_ELSE(mlib_is_not_cxx()) - -/** - * @brief Use as the prefix of a braced initializer within C headers, allowing - * the initializer to appear as a compound-init in C and an equivalent braced - * aggregate-init in C++ - */ -#define mlib_init(T) MLIB_LANG_PICK((T))(T) - -/** - * @brief Expands to `noexcept` when compiled as C++, otherwise expands to - * nothing - */ -#define mlib_noexcept MLIB_IF_CXX(noexcept) - -#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) -#define mlib_is_little_endian() (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) -#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) -#define mlib_is_little_endian() (__BYTE_ORDER == __LITTLE_ENDIAN) -#elif defined(_WIN32) -#define mlib_is_little_endian() 1 -#else -#error "Do not know how to detect endianness on this platform." -#endif - -// clang-format off -/** - * @brief Expands to a static assertion declaration. - * - * When supported, this can be replaced with `_Static_assert` or `static_assert` - */ -#define mlib_static_assert(...) MLIB_ARGC_PICK (_mlib_static_assert, __VA_ARGS__) -#define _mlib_static_assert_argc_1(Expr) \ - _mlib_static_assert_argc_2 ((Expr), "Static assertion failed") -#define _mlib_static_assert_argc_2(Expr, Msg) \ - extern int \ - MLIB_PASTE (_mlib_static_assert_placeholder, __COUNTER__)[(Expr) ? 2 : -1] \ - MLIB_IF_GNU_LIKE (__attribute__ ((unused))) -// clang-format on - -#define mlib_extern_c_begin() MLIB_IF_CXX(extern "C" {) mlib_static_assert(1, "") -#define mlib_extern_c_end() MLIB_IF_CXX( \ - }) mlib_static_assert(1, "") - -#ifdef __GNUC__ -#define mlib_is_gnu_like() 1 -#ifdef __clang__ -#define mlib_is_gcc() 0 -#define mlib_is_clang() 1 -#else -#define mlib_is_gcc() 1 -#define mlib_is_clang() 0 -#endif -#define mlib_is_msvc() 0 -#elif defined(_MSC_VER) -#define mlib_is_gnu_like() 0 -#define mlib_is_clang() 0 -#define mlib_is_gcc() 0 -#define mlib_is_msvc() 1 -#endif - -#if defined(_WIN32) -#define mlib_is_win32() 1 -#define mlib_is_unix() 0 -#else -#define mlib_is_unix() 1 -#define mlib_is_win32() 0 -#endif - -#define MLIB_IF_CLANG(...) MLIB_IF_ELSE(mlib_is_clang())(__VA_ARGS__)(MLIB_NOTHING(#__VA_ARGS__)) -#define MLIB_IF_GCC(...) MLIB_IF_ELSE(mlib_is_gcc())(__VA_ARGS__)(MLIB_NOTHING(#__VA_ARGS__)) -#define MLIB_IF_GNU_LIKE(...) MLIB_IF_GCC(__VA_ARGS__) MLIB_IF_CLANG(__VA_ARGS__) MLIB_NOTHING(#__VA_ARGS__) -#define MLIB_IF_UNIX_LIKE(...) MLIB_IF_ELSE(mlib_is_unix())(__VA_ARGS__)(MLIB_NOTHING(#__VA_ARGS__)) - -// note: Bug on GCC preprocessor prevents us from using if/else trick to omit MSVC code -#if mlib_is_msvc() -#define MLIB_IF_MSVC(...) __VA_ARGS__ -#define mlib_pragma(...) __pragma(__VA_ARGS__) mlib_static_assert(1, "") -#else -#define MLIB_IF_MSVC(...) MLIB_NOTHING(#__VA_ARGS__) -#define mlib_pragma(...) _Pragma(#__VA_ARGS__) mlib_static_assert(1, "") -#endif - -#define MLIB_PRAGMA_IF_CLANG(...) MLIB_IF_CLANG(_Pragma(#__VA_ARGS__)) -#define MLIB_PRAGMA_IF_GCC(...) MLIB_IF_GCC(_Pragma(#__VA_ARGS__)) -#define MLIB_PRAGMA_IF_GNU_LIKE(...) MLIB_IF_GNU_LIKE(_Pragma(#__VA_ARGS__)) -#define MLIB_PRAGMA_IF_UNIX_LIKE(...) MLIB_IF_UNIX_LIKE(_Pragma(#__VA_ARGS__)) -#define MLIB_PRAGMA_IF_MSVC(...) MLIB_IF_MSVC(__pragma(__VA_ARGS__)) - -#define MLIB_FUNC MLIB_IF_GNU_LIKE(__func__) MLIB_IF_MSVC(__FUNCTION__) - -#define mlib_diagnostic_push() \ - MLIB_IF_GNU_LIKE(mlib_pragma(GCC diagnostic push);) \ - MLIB_IF_MSVC(mlib_pragma(warning(push));) \ - mlib_static_assert(1, "") - -#define mlib_diagnostic_pop() \ - MLIB_IF_GNU_LIKE(mlib_pragma(GCC diagnostic pop);) \ - MLIB_IF_MSVC(mlib_pragma(warning(pop));) \ - mlib_static_assert(1, "") - -#define mlib_gcc_warning_disable(Warning) \ - MLIB_IF_GCC(mlib_pragma(GCC diagnostic ignored Warning);) \ - mlib_static_assert(1, "") - -#define mlib_gnu_warning_disable(Warning) \ - MLIB_IF_GNU_LIKE(mlib_pragma(GCC diagnostic ignored Warning);) \ - mlib_static_assert(1, "") - -#define mlib_msvc_warning(...) \ - MLIB_IF_MSVC(mlib_pragma(warning(__VA_ARGS__));) \ - mlib_static_assert(1, "") - -/** - * @brief Attribute macro that forces the function to be inlined at all call sites. - * - * Don't use this unless you really know that you need it, lest you generate code - * bloat when the compiler's heuristics would do a better job. - */ -#define mlib_always_inline MLIB_IF_GNU_LIKE(__attribute__((always_inline)) inline) MLIB_IF_MSVC(__forceinline) - -// Annotate a variable as thread-local -#define mlib_thread_local MLIB_IF_GNU_LIKE(__thread) MLIB_IF_MSVC(__declspec(thread)) - -// Annotate an entiry that might be unused -#define mlib_maybe_unused MLIB_IF_GNU_LIKE(__attribute__((unused))) - -// clang-format off -/** - * @brief Expand to `1` if the current build configuration matches the given token. - * - * If the token is not a known valid build configuration, generates a compilation - * error (check your spelling!) - * - * Requires that `_MLIB_BUILD_CONFIG` is defined, otherwise always expands to `0` - */ -#define mlib_build_config_is(Config) \ - /* If `Config` is a recognized config, this line will disappear, */ \ - /* other wise it will be a "call to undefined macro": */ \ - MLIB_PASTE_4 (_mlibTestBuildConfig_, Config, _, Config) () \ - /* If `Config` is the same token as `_MLIB_BUILD_CONFIG`, this will */ \ - /* expand to `1`, otherwise it will expand to `0` */ \ - MLIB_IS_EMPTY (MLIB_PASTE_4 (_mlibTestBuildConfig_, Config, _, _MLIB_BUILD_CONFIG) ()) -// clang-format on -// Known build configurations: -#define _mlibTestBuildConfig_Release_Release() -#define _mlibTestBuildConfig_Debug_Debug() -#define _mlibTestBuildConfig_RelWithDebInfo_RelWithDebInfo() -#define _mlibTestBuildConfig_MinSizeRel_MinSizeRel() - -/** - * @brief Emit a _Pragma that will disable warnings about the use of deprecated entities. - */ -#define mlib_disable_deprecation_warnings() \ - mlib_gnu_warning_disable("-Wdeprecated-declarations"); \ - mlib_msvc_warning(disable : 4996) - -/** - * @brief Function-like macro that expands to `1` if we are certain that we are - * compiling with optimizations enabled. - * - * This may yield `0` if we cannot determine whether optimization is turned on. - * - * This macro should be used with care, as different translation units can see different values, - * but still be linked together in the final program. Beware generating ODR violations. - */ -#define mlib_is_optimized_build() _mlibIsOptimizedBuild() - -#if mlib_build_config_is(Release) || mlib_build_config_is(RelWithDebInfo) || mlib_build_config_is(MinSizeRel) || \ - (defined(__OPTIMIZE__) && __OPTIMIZE__) -// Preproc definition __OPTIMIZE__set by GCC ang Clang when the optimizer is enabled. -// MSVC has no such definition, so we rely on CMake to tell us when we are compiling in release mode -#define _mlibIsOptimizedBuild() 1 -#else -#define _mlibIsOptimizedBuild() 0 -#endif - -#if mlib_is_gnu_like() -#define mlib_have_typeof() 1 -#elif defined _MSC_VER && _MSC_VER >= 1939 && !__cplusplus -// We can __typeof__ in MSVC 19.39+ -#define mlib_have_typeof() 1 -#else -#define mlib_have_typeof() 0 -#endif - -/** - * @brief Equivalent to C23's `typeof()`, if it is supported by the current compiler. - * - * This expands to `__typeof__`, which is supported even on newer MSVC compilers, - * even when not in C23 mode. - */ -#define mlib_typeof(...) MLIB_IF_ELSE(mlib_have_typeof())(__typeof__)(__mlib_typeof_is_not_supported)(__VA_ARGS__) - -/** - * @brief Disable warnings for constant conditional expressions. - */ -#define mlib_disable_constant_conditional_expression_warnings() mlib_msvc_warning(disable : 4127) - -/** - * @brief Disable warnings for potentially unused parameters. - */ -#define mlib_disable_unused_parameter_warnings() \ - MLIB_IF_GNU_LIKE(mlib_gnu_warning_disable("-Wunused-parameter");) \ - MLIB_IF_MSVC(mlib_msvc_warning(disable : 4100);) mlib_static_assert(1, "") - -#if mlib_is_clang() -#define mlib_printf_attribute(f, v) __attribute__((format(printf, f, v))) -#elif mlib_is_gcc() -#define mlib_printf_attribute(f, v) __attribute__((format(gnu_printf, f, v))) -#else -#define mlib_printf_attribute(f, v) -#endif - -/** - * @brief Annotate a boolean expression as "likely to be true" to guide the optimizer. - * Use this very sparingly. - */ -#define mlib_likely(...) MLIB_IF_ELSE(mlib_is_gnu_like())(__builtin_expect(!!(__VA_ARGS__), 1))((__VA_ARGS__)) -/** - * @brief Annotate a boolean expression as "likely to be untrue" to guide the optimizer. - * Use this very sparingly. - */ -#define mlib_unlikely(...) MLIB_IF_ELSE(mlib_is_gnu_like())(__builtin_expect(!!(__VA_ARGS__), 0))((__VA_ARGS__)) - -#endif // MLIB_CONFIG_H_INCLUDED diff --git a/bsonjs/common/mlib/duration.h b/bsonjs/common/mlib/duration.h deleted file mode 100644 index f2496fe..0000000 --- a/bsonjs/common/mlib/duration.h +++ /dev/null @@ -1,399 +0,0 @@ -/** - * @file mlib/duration.h - * @brief Duration types and functions - * @date 2025-04-17 - * - * This file contains types and functions for working with a "duration" type, - * which represents an elapsed amount of time, possibly negative. - * - * The type `mlib_duration_rep_t` is a typedef of the intregral type that is - * used to represent duration units. - * - * The `mlib_duration` is a trivial object that represents a duration of time. - * The internal representation should not be inspected outside of this file. - * - * @copyright Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MLIB_DURATION_H_INCLUDED -#define MLIB_DURATION_H_INCLUDED - -#include -#include -#include -#include - -#include -#include - -mlib_extern_c_begin(); - -/** - * @brief The integral type used to represent a count of units of time. - */ -typedef int64_t mlib_duration_rep_t; - -/** - * @brief Represents a duration of time, either positive, negative, or zero. - * - * @note A zero-initialized (static initialized) duration represents the zero - * duration (no elapsed time) - * - * @note The time representation is intended to be abstract, and should be - * converted to concrete units of time by calling the `_count` functions. - */ -typedef struct mlib_duration { - /** - * @brief The integral representation of the duration. - * - * Do not read or modify this field except to zero-initialize it. - */ - mlib_duration_rep_t _rep; -} mlib_duration; - -/** - * @brief A macro that expands to the maximum positive duration - */ -#define mlib_duration_max() (mlib_init(mlib_duration){mlib_maxof(mlib_duration_rep_t)}) -/** - * @brief A macro that expands to the minimum duration (a negative duration) - */ -#define mlib_duration_min() (mlib_init(mlib_duration){mlib_minof(mlib_duration_rep_t)}) - -/** - * @brief Obtain the count of microseconds represented by the duration (round - * toward zero) - */ -static inline mlib_duration_rep_t -mlib_microseconds_count(const mlib_duration dur) mlib_noexcept -{ - return dur._rep; -} - -/** - * @brief Obtain the count of milliseconds represented by the duration (round - * toward zero) - */ -static inline mlib_duration_rep_t -mlib_milliseconds_count(const mlib_duration dur) mlib_noexcept -{ - return mlib_microseconds_count(dur) / 1000; -} - -/** - * @brief Obtain the count of seconds represented by the duration (rounded - * toward zero) - */ -static inline mlib_duration_rep_t -mlib_seconds_count(const mlib_duration dur) mlib_noexcept -{ - return mlib_milliseconds_count(dur) / 1000; -} - -/** - * @brief Duration creation and manipulation shorthands - * - * This function-like macro is used to create and manipulate durations on-the-fly. - * It can be called with the following syntaxes: - * - * - `mlib_duration()` (no arguments) - * creates a zero-valued duration - * - `mlib_duration()` - * copies the duration object `` - * - `mlib_duration(, )` - * Creates a duration of `` instances of ``. - * - `mlib_duration(, , )` - * Manipulates a duration according to ``. - * - * In the above, `` may be a parenthesized `mlib_duration` argument list or a - * duration object; `` must be an integral expression and `` is a - * unit suffix identifer (see: `mlib_duration_with_unit`) to create a duration - * of `` instances of ``, and `` is one of: - * - * - `plus`/`minus` to add/subtract two durations. - * - `mul`/`div` to multiply/divide a duration by a scalar factor. - * - `min`/`max` to get the minimum/maximum between two durations. - * - * All duration arithmetic/conversion operations use well-defined saturating - * arithmetic, and never wrap or trap. - */ -#define mlib_duration(...) MLIB_EVAL_16(_mlibDurationMagic(__VA_ARGS__)) -#define _mlibDurationMagic(...) \ - MLIB_DEFERRED(MLIB_ARGC_PASTE(_mlib_duration, __VA_ARGS__)) \ - (__VA_ARGS__) -// Wraps a `` argument, and expands to the magic only if it is parenthesized -#define _mlibDurationArgument(X) \ - /* If given a parenthesized expression, act as an invocation of `mlib_duration() */ \ - MLIB_IF_ELSE(MLIB_IS_PARENTHESIZED(X)) \ - /* then: */ (_mlibDurationMagic X) /* else: */ (X) - -// Wrap a macro argument that should support the duration DSL -#define mlib_duration_arg(X) MLIB_EVAL_16(_mlibDurationArgument(X)) - -// Zero arguments, just return a zero duration: -#define _mlib_duration_argc_0() (mlib_init(mlib_duration){0}) -// One argument, just copy the duration. Passing through a function forces the type to be correct -#define _mlib_duration_argc_1(D) _mlibDurationCopy(D) -// Two arguments, the second arg is a unit suffix: -#define _mlib_duration_argc_2(Count, Unit) mlib_duration_with_unit(Count, Unit) -// Three arguments, an infix operation: -#define _mlib_duration_argc_3(Duration, Operator, Operand) \ - MLIB_DEFERRED(MLIB_PASTE(_mlibDurationInfixOperator_, Operator)) \ - (Duration, Operand) - -// By-value copy a duration -static inline mlib_duration -_mlibDurationCopy(mlib_duration d) -{ - return d; -} - -// Duration scalar multiply -#define _mlibDurationInfixOperator_mul(LHS, Fac) \ - _mlibDurationMultiply(_mlibDurationArgument(LHS), mlib_upsize_integer(Fac)) -static inline mlib_duration -_mlibDurationMultiply(const mlib_duration dur, mlib_upsized_integer fac) mlib_noexcept -{ - mlib_duration ret = {0}; - const bool overflowed = fac.is_signed ? mlib_mul(&ret._rep, dur._rep, fac.bits.as_signed) - : mlib_mul(&ret._rep, dur._rep, fac.bits.as_unsigned); - if (overflowed) { - if ((dur._rep < 0) != (fac.is_signed && fac.bits.as_signed < 0)) { - // Different signs: Neg × Pos = Neg - ret = mlib_duration_min(); - } else { - // Same signs: Pos × Pos = Pos - // Neg × Neg = Pos - ret = mlib_duration_max(); - } - } - return ret; -} - -// Duration scalar divide -#define _mlibDurationInfixOperator_div(LHS, Div) \ - _mlibDurationDivide(_mlibDurationArgument(LHS), mlib_upsize_integer(Div)) -static inline mlib_duration -_mlibDurationDivide(mlib_duration a, mlib_upsized_integer div) mlib_noexcept -{ - mlib_check(div.bits.as_unsigned, neq, 0); - if ((div.is_signed && div.bits.as_signed == -1) // - && a._rep == mlib_minof(mlib_duration_rep_t)) { - // MIN / -1 is UB, but the saturating result is the max - a = mlib_duration_max(); - } else { - if (div.is_signed) { - a._rep /= div.bits.as_signed; - } else { - a._rep = (mlib_duration_rep_t)((uintmax_t)a._rep / div.bits.as_unsigned); - } - } - return a; -} - -// Duration addition -#define _mlibDurationInfixOperator_plus(LHS, RHS) \ - _mlibDurationAdd(_mlibDurationArgument(LHS), _mlibDurationArgument(RHS)) -static inline mlib_duration -_mlibDurationAdd(const mlib_duration a, const mlib_duration b) mlib_noexcept -{ - mlib_duration ret = {0}; - if (mlib_add(&ret._rep, a._rep, b._rep)) { - if (a._rep > 0) { - ret = mlib_duration_max(); - } else { - ret = mlib_duration_min(); - } - } - return ret; -} - -// Duration subtraction -#define _mlibDurationInfixOperator_minus(LHS, RHS) \ - _mlibDurationSubtract(_mlibDurationArgument(LHS), _mlibDurationArgument(RHS)) -static inline mlib_duration -_mlibDurationSubtract(const mlib_duration a, const mlib_duration b) mlib_noexcept -{ - mlib_duration ret = {0}; - if (mlib_sub(&ret._rep, a._rep, b._rep)) { - if (a._rep < 0) { - ret = mlib_duration_min(); - } else { - ret = mlib_duration_max(); - } - } - return ret; -} - -#define _mlibDurationInfixOperator_min(Duration, RHS) \ - _mlibDurationMinBetween(_mlibDurationArgument(Duration), _mlibDurationArgument(RHS)) -static inline mlib_duration -_mlibDurationMinBetween(mlib_duration lhs, mlib_duration rhs) -{ - if (lhs._rep < rhs._rep) { - return lhs; - } - return rhs; -} - -#define _mlibDurationInfixOperator_max(Duration, RHS) \ - _mlibDurationMaxBetween(_mlibDurationArgument(Duration), _mlibDurationArgument(RHS)) -static inline mlib_duration -_mlibDurationMaxBetween(mlib_duration lhs, mlib_duration rhs) -{ - if (lhs._rep > rhs._rep) { - return lhs; - } - return rhs; -} - -/** - * @brief Create a duration object from a count of some unit of time - * - * @param Count An integral expression - * @param Unit A unit suffix identifier, must be one of: - * - * - `ns` (nanoseconds) - * - `us` (microseconds) - * - `ms` (milliseconds) - * - `s` (seconds) - * - `mn` (minutes) - * - `h` (hours) - * - * Other unit suffixes will generate a compile-time error - */ -#define mlib_duration_with_unit(Count, Unit) \ - MLIB_PASTE(_mlibCreateDurationFromUnitCount_, Unit)(mlib_upsize_integer(Count)) - -static inline mlib_duration -_mlibCreateDurationFromUnitCount_us(const mlib_upsized_integer n) mlib_noexcept -{ - mlib_duration ret = mlib_duration(); - if (n.is_signed) { - // The duration rep is the same as the signed max type, so we don't need to do any - // special arithmetic to encode it - mlib_static_assert(sizeof(mlib_duration_rep_t) == sizeof(n.bits.as_signed)); - ret._rep = mlib_assert_narrow(mlib_duration_rep_t, n.bits.as_signed); - } else { - if (mlib_narrow(&ret._rep, n.bits.as_unsigned)) { - // Unsigned value is too large to fit in our signed repr, so just use the max repr - ret = mlib_duration_max(); - } - } - return ret; -} - -static inline mlib_duration -_mlibCreateDurationFromUnitCount_ns(mlib_upsized_integer n) mlib_noexcept -{ - // We encode as a count of microseconds, so we lose precision here. - if (n.is_signed) { - n.bits.as_signed /= 1000; - } else { - n.bits.as_unsigned /= 1000; - } - return _mlibCreateDurationFromUnitCount_us(n); -} - -static inline mlib_duration -_mlibCreateDurationFromUnitCount_ms(const mlib_upsized_integer n) mlib_noexcept -{ - return mlib_duration(_mlibCreateDurationFromUnitCount_us(n), mul, 1000); -} - -static inline mlib_duration -_mlibCreateDurationFromUnitCount_s(const mlib_upsized_integer n) -{ - return mlib_duration(_mlibCreateDurationFromUnitCount_us(n), mul, 1000 * 1000); -} - -static inline mlib_duration -_mlibCreateDurationFromUnitCount_mn(const mlib_upsized_integer n) -{ - return mlib_duration(_mlibCreateDurationFromUnitCount_us(n), mul, 60 * 1000 * 1000); -} - -static inline mlib_duration -_mlibCreateDurationFromUnitCount_h(const mlib_upsized_integer n) -{ - return mlib_duration(_mlibCreateDurationFromUnitCount_mn(n), mul, 60); -} - -/** - * @brief Compare two durations - * - * @retval <0 If `a` is less-than `b` - * @retval >0 If `b` is less-than `a` - * @retval 0 If `a` and `b` are equal durations - * - * @note This is a function-like macro that can be called with an infix operator - * as the second argument to do natural duration comparisons: - * - * ``` - * mlib_duration_cmp(, , ) - * ``` - * - * Where each `` should be an arglist for @see mlib_duration - */ -static inline enum mlib_cmp_result -mlib_duration_cmp(const mlib_duration a, const mlib_duration b) mlib_noexcept -{ - return mlib_cmp(a._rep, b._rep); -} - -#define mlib_duration_cmp(...) MLIB_ARGC_PICK(_mlibDurationCmp, __VA_ARGS__) -#define _mlibDurationCmp_argc_2 mlib_duration_cmp -#define _mlibDurationCmp_argc_3(Left, Op, Right) \ - (mlib_duration_cmp(mlib_duration_arg(Left), mlib_duration_arg(Right)) Op 0) - -/** - * @brief Obtain an mlib_duration that corresponds to a `timespec` value - * - * @note The `timespec` type may represent times outside of the range of, or - * more precise than, what is representable in `mlib_duration`. In such case, - * the returned duration will be the nearest representable duration, rounded - * toward zero. - */ -static inline mlib_duration -mlib_duration_from_timespec(const struct timespec ts) mlib_noexcept -{ - return mlib_duration((ts.tv_sec, s), plus, (ts.tv_nsec, ns)); -} - -/** - * @brief Create a C `struct timespec` that corresponds to the given duration - * - * @param d The duration to be converted - * @return struct timespec A timespec that represents the same durations - */ -static inline struct timespec -mlib_duration_to_timespec(const mlib_duration d) mlib_noexcept -{ - // Number of full seconds in the duration - const mlib_duration_rep_t n_full_seconds = mlib_seconds_count(d); - // Duration with full seconds removed - const mlib_duration usec_part = mlib_duration(d, minus, (n_full_seconds, s)); - // Number of microseconds in the duration, minus all full seconds - const mlib_duration_rep_t n_remaining_microseconds = mlib_microseconds_count(usec_part); - // Compute the number of nanoseconds: - const int32_t n_nsec = mlib_assert_mul(int32_t, n_remaining_microseconds, 1000); - struct timespec ret; - ret.tv_sec = n_full_seconds; - ret.tv_nsec = n_nsec; - return ret; -} - -mlib_extern_c_end(); - -#endif // MLIB_DURATION_H_INCLUDED diff --git a/bsonjs/common/mlib/intencode.h b/bsonjs/common/mlib/intencode.h deleted file mode 100644 index f26cae5..0000000 --- a/bsonjs/common/mlib/intencode.h +++ /dev/null @@ -1,350 +0,0 @@ -/** - * @file mlib/intencode.h - * @brief Integer encoding functions - * @date 2025-01-31 - * - * @copyright Copyright (c) 2025 - * - * 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. - */ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -/** - * @brief Decode an unsigned 32-bit little-endian integer from a memory buffer - */ -static inline uint32_t -mlib_read_u32le(const void *buf) -{ - uint32_t ret = 0; - if (mlib_is_little_endian()) { - // Optimize: The platform uses a LE encoding already - memcpy(&ret, buf, sizeof ret); - } else { - // Portable decode of an LE integer - const uint8_t *cptr = (const uint8_t *)buf; - mlib_foreach_urange (i, sizeof ret) { - ret <<= 8; - ret |= cptr[(sizeof ret) - i - 1]; - } - } - return ret; -} - -/** - * @brief Decode an signed 32-bit little-endian integer from a memory buffer - */ -static inline int32_t -mlib_read_i32le(const void *buf) -{ - const uint32_t u = mlib_read_u32le(buf); - int32_t r; - memcpy(&r, &u, sizeof r); - return r; -} - -/** - * @brief Decode an unsigned 64-bit little-endian integer from a memory buffer - */ -static inline uint64_t -mlib_read_u64le(const void *buf) -{ - uint64_t ret = 0; - if (mlib_is_little_endian()) { - // Optimize: The platform uses a LE encoding already - memcpy(&ret, buf, sizeof ret); - } else { - // Portable decode of an LE integer - const uint8_t *cptr = (const uint8_t *)buf; - mlib_foreach_urange (i, sizeof ret) { - ret <<= 8; - ret |= cptr[(sizeof ret) - i - 1]; - } - } - return ret; -} - -/** - * @brief Decode an signed 64-bit little-endian integer from a memory buffer - */ -static inline int64_t -mlib_read_i64le(const void *buf) -{ - const uint64_t u = mlib_read_u64le(buf); - int64_t r; - memcpy(&r, &u, sizeof r); - return r; -} - -/** - * @brief Write an unsigned 32-bit little-endian integer into a destination - * - * @return void* The address after the written value - */ -static inline void * -mlib_write_u32le(void *out, const uint32_t value) -{ - uint8_t *o = (uint8_t *)out; - if (mlib_is_little_endian()) { - memcpy(o, &value, sizeof value); - return o + sizeof value; - } - mlib_foreach_urange (i, sizeof value) { - *o++ = (value >> (8u * i)) & 0xffu; - } - return o; -} - -/** - * @brief Write a signed 32-bit little-endian integer into a destination - * - * @return void* The address after the written value - */ -static inline void * -mlib_write_i32le(void *out, int32_t value) -{ - return mlib_write_u32le(out, (uint32_t)value); -} - -/** - * @brief Write an unsigned 64-bit little-endian integer into a destination - * - * @return void* The address after the written value - */ -static inline void * -mlib_write_u64le(void *out, const uint64_t value) -{ - uint8_t *o = (uint8_t *)out; - if (mlib_is_little_endian()) { - memcpy(o, &value, sizeof value); - return o + sizeof value; - } - mlib_foreach_urange (i, sizeof value) { - *o++ = (value >> (8u * i)) & 0xffu; - } - return o; -} - -/** - * @brief Write an signed 64-bit little-endian integer into a destination - * - * @return void* The address after the written value - */ -static inline void * -mlib_write_i64le(void *out, int64_t value) -{ - return mlib_write_u64le(out, (uint64_t)value); -} - -/** - * @brief Write a little-endian 64-bit floating point (double) to the given - * memory location - * - * @return void* The address after the written value. - */ -static inline void * -mlib_write_f64le(void *out, double d) -{ - mlib_static_assert(sizeof(double) == sizeof(uint64_t)); - uint64_t bits; - memcpy(&bits, &d, sizeof d); - return mlib_write_u64le(out, bits); -} - -/** - * @brief Decode a 64-bit natural number - * - * @param in The input string to be decoded. Does not support a sign or base prefix! - * @param base The base to be decoded. Must not be zero! - * @param out Pointer that receives the decoded value - * @return int A result code for the operation. - * - * See `mlib_i64_parse` for more details. - */ -static inline int -mlib_nat64_parse(mstr_view in, unsigned base, uint64_t *out) -{ - if (in.len == 0) { - // Empty string is not valid - return EINVAL; - } - - // Accummulate into this value: - uint64_t value = 0; - // Whether any operation in the parse overflowed the integer value - bool did_overflow = false; - // Loop until we have consumed the full string, or encounter an invalid digit - while (in.len) { - // Shift place value for another digit - did_overflow = mlib_mul(&value, base) || did_overflow; - // Case-fold for alpha digits - int32_t digit = mlib_latin_tolower(in.data[0]); - unsigned digit_value = 0; - // Only standard digits - if (digit >= '0' && digit <= '9') { - // Normal digit - digit_value = (unsigned)(digit - '0'); - } else if (digit >= 'a' && digit <= 'z') { - // Letter digits - digit_value = (unsigned)(digit - 'a') + 10; - } else { - // Not a valid alnum digit - return EINVAL; - } - if (digit_value >= base) { - // The digit value is out-of-range for our chosen base - return EINVAL; - } - // Accumulate the new digit value - did_overflow = mlib_add(&value, digit_value) || did_overflow; - // Jump to the next digit in the string - in = mstr_substr(in, 1); - } - - if (did_overflow) { - return ERANGE; - } - - (void)(out && (*out = value)); - return 0; -} - -/** - * @brief Parse a string as a 64-bit signed integer - * - * @param in The string of digits to be parsed. - * @param base Optional: The base to use for parsing. Use "0" to infer the base. - * @param out Optional storage for an int64 value to be updated with the result - * @return int Returns an errno value for the parse - * - * - A value of `0` indicates that the parse was successful. - * - A value of `EINVAL` indicates that the input string is not a valid - * representation of an integer. - * - A value of `ERANGE` indicates that the input string is a valid integer, - * but the actual encoded value cannot be represented in an `int64_t` - * - If the parse fails (returns non-zero), then the value at `*out` will remain - * unmodified. - * - * This differs from `strtoll` in that it requires that the entire string be - * parsed as a valid integer. If parsing stops early, then the result will indicate - * an error of EINVAL. - */ -static inline int -mlib_i64_parse(mstr_view in, unsigned base, int64_t *out) -{ - if (in.len == 0) { - // Empty string is not a valid integer - return EINVAL; - } - // Parse the possible sign prefix - int sign = 1; - // Check for a "+" - if (in.data[0] == '+') { - // Just a plus. Drop it and do nothing with it. - in = mstr_substr(in, 1); - } - // Check for a negative prefix - else if (in.data[0] == '-') { - // Negative sign. We'll negate the value later. - in = mstr_substr(in, 1); - sign = -1; - } - - // Infer the base value, if we have one - if (base == 0) { - if (in.len && in.data[0] == '0') { - if (in.len > 1) { - if (mlib_latin_tolower(in.data[1]) == 'x') { - // Hexadecimal - base = 16; - in = mstr_substr(in, 2); - } else if (mlib_latin_tolower(in.data[1]) == 'o') { - // Octal - base = 8; - in = mstr_substr(in, 2); - } else if (mlib_latin_tolower(in.data[1]) == 'b') { - // Binary - base = 2; - in = mstr_substr(in, 2); - } - } - if (base == 0) { - // Other: Octal with a single "0" prefix. Don't trim this, because - // it may be a literal "0" - base = 8; - } - } else { - // No '0' prefix. Treat it as decimal - base = 10; - } - } - - // Try to parse the natural number now that we have removed all prefixes and - // have a non-zero base. - uint64_t nat; - int rc = mlib_nat64_parse(in, base, &nat); - if (rc) { - return rc; - } - - // Try to narrow from the u64 to i64 and apply the sign. This must be done as - // one operation because of the pathological case of parsing INT64_MIN - int64_t i64 = 0; - if (mlib_mul(&i64, nat, sign)) { - return ERANGE; - } - - (void)(out && (*out = i64)); - return 0; -} - -#define mlib_i64_parse(...) MLIB_ARGC_PICK(_mlib_i64_parse, __VA_ARGS__) -#define _mlib_i64_parse_argc_2(S, Ptr) _mlib_i64_parse_argc_3((S), 0, (Ptr)) -#define _mlib_i64_parse_argc_3(S, Base, Ptr) mlib_i64_parse(mstr_view_from((S)), Base, Ptr) - -/** - * @brief Parse a 32-bit integer from a string. - * - * See `mlib_i64_parse` for more details. - */ -static inline int -mlib_i32_parse(mstr_view in, unsigned base, int32_t *out) -{ - int64_t tmp; - int ec = mlib_i64_parse(in, base, &tmp); - if (ec) { - // Failed to parse the int64 value. - return ec; - } - // Attempt to narrow to a 32-bit value - int32_t i32 = 0; - if (mlib_narrow(&i32, tmp)) { - // Value is out-of-range - return ERANGE; - } - // Success - (void)(out && (*out = i32)); - return 0; -} - -#define mlib_i32_parse(...) MLIB_ARGC_PICK(_mlib_i32_parse, __VA_ARGS__) -#define _mlib_i32_parse_argc_2(S, Ptr) _mlib_i32_parse_argc_3((S), 0, (Ptr)) -#define _mlib_i32_parse_argc_3(S, Base, Ptr) mlib_i32_parse(mstr_view_from((S)), Base, Ptr) diff --git a/bsonjs/common/mlib/intutil.h b/bsonjs/common/mlib/intutil.h deleted file mode 100644 index bd39978..0000000 --- a/bsonjs/common/mlib/intutil.h +++ /dev/null @@ -1,145 +0,0 @@ -/** - * @file mlib/intutil.h - * @brief Integer utilities - * @date 2025-01-28 - * - * @copyright Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MLIB_INTUTIL_H_INCLUDED -#define MLIB_INTUTIL_H_INCLUDED - -#include - -#include -#include -#include - -/** - * @brief Given an integral type, evaluates to `true` if that type is signed, - * otherwise `false` - */ -#define mlib_is_signed(T) (!((T)(-1) > 0)) - -/** - * @brief Like `sizeof`, but returns the number of bits in the object representation - */ -#define mlib_bitsizeof(T) ((sizeof(T)) * ((size_t)CHAR_BIT)) - -// clang-format off -/** - * @brief Generate a mask of contiguous bits. - * - * @param NumOnes The non-negative number of contiguous 1 bits - * @param NumZeros The non-negative number of contiguous 0 bits to set in the low position - * - * The generated mask is of the form: - * - * NumZeros - * │ - * ┌┴─┐ - * │ │ - * `0..0 1..1 0..0` - * │ │ - * └┬─┘ - * │ - * NumOnes - * - * Explain the arithmetic below: - * - * 1. `ones = 0b1111...` : All high bits - * 2. `tmp = ones >> (NumOnes - num_bits_of(ones))` : Truncate to the number of 1s we want - * 3. `res = tmp << NumZeros` : Add the 0s in the low position - */ -#define mlib_bits(NumOnes, NumZeros) ( \ - ((NumOnes) \ - ? (~UINTMAX_C(0) >> ((mlib_bitsizeof(uintmax_t) - (uintmax_t)(NumOnes)))) \ - : 0) \ - << ((uintmax_t)(NumZeros))) - -/** - * @brief Given an integral type, yield an integral constant value representing - * the maximal value of that type. - */ -#define mlib_maxof(T) \ - ((T) (mlib_is_signed (T) \ - ? ((T) mlib_bits(mlib_bitsizeof(T) - 1u, 0)) \ - : ((T) mlib_bits(mlib_bitsizeof(T), 0)))) - -/** - * @brief Given an integral type, yield an integral constant value for the - * minimal value of that type. - */ -#define mlib_minof(T) \ - ((T) (!mlib_is_signed (T) \ - ? (T) 0 \ - : (T) mlib_bits(1, mlib_bitsizeof(T) - 1u))) -// clang-format on - -/** - * @brief A container for an integer that has been "scaled up" to maximum precision - * - * Don't create this manually. Instead, use `mlib_upsize_integer` to do it automatically - */ -typedef struct mlib_upsized_integer { - union { - // The signed value of the integer - intmax_t as_signed; - // The unsigned value of the integer - uintmax_t as_unsigned; - } bits; - // Whether the upscaled integer bits should be treated as a two's complement signed integer - bool is_signed; -} mlib_upsized_integer; - -// clang-format off -/** - * @brief Create an "upsized" version of an integer, normalizing all integral - * values into a single type so that we can deduplicate functions that operate - * on disparate integer types. - * - * Details: The integer is upcast into the maximum precision integer type (intmax_t). If - * the operand is smaller than `intmax_t`, we assume that casting to the signed `intmax_t` - * is always safe, even if the operand is unsigned, since e.g. a u32 can always be cast to - * an i64 losslessly. - * - * If the integer to upcast is the same size as `intmax_t`, we need to decide whether to store - * it as unsigned. The expression `(_mlibGetOne(Value)) - 2 < 1` will be `true` iff the operand is signed, - * otherwise false. If the operand is signed, we can safely cast to `intmax_t` (it probably already - * is of that type), otherwise, we cast to `uintmax_t` and the returned `mlib_upsized_integer` will - * indicate that the stored value is unsigned. The expression `1 - 2 < 1` is chosen - * to avoid `-Wtype-limits` warnings from some compilers about unsigned comparison. - */ -#define mlib_upsize_integer(Value) \ - mlib_upsize_integer((uintmax_t)(intmax_t)((Value)), _mlibShouldTreatBitsAsSigned(Value)) -#define _mlibShouldTreatBitsAsSigned(Value) \ - /* NOLINTNEXTLINE(bugprone-sizeof-expression) */ \ - (sizeof ((Value)) < sizeof (intmax_t) || (_mlibGetOne(Value) - 2) < _mlibGetOne(Value)) -// Yield a 1 value of similar-ish type to the given expression. The ternary -// forces an integer promotion of literal 1 match the type of `V`, while leaving -// `V` unevaluated. Note that this will also promote `V` to be at least `(unsigned) int`, -// so the 1 value is only "similar" to `V`, and may be of a larger type -#define _mlibGetOne(V) (1 ? 1 : (V)) -// Function impl for upsize_integer -static inline mlib_upsized_integer -(mlib_upsize_integer) (uintmax_t bits, bool treat_as_signed) -{ - mlib_upsized_integer ret; - ret.bits.as_unsigned = bits; - ret.is_signed = treat_as_signed; - return ret; -} -// clang-format on - -#endif // MLIB_INTUTIL_H_INCLUDED diff --git a/bsonjs/common/mlib/loop.h b/bsonjs/common/mlib/loop.h deleted file mode 100644 index 1c4d0ef..0000000 --- a/bsonjs/common/mlib/loop.h +++ /dev/null @@ -1,165 +0,0 @@ -/** - * @file mlib/loop.h - * @brief Looping utility macros - * @date 2025-01-29 - * - * @copyright Copyright (c) 2025 - * - * 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. - */ -#ifndef MLIB_LOOP_H_INCLUDED -#define MLIB_LOOP_H_INCLUDED - -#include - -#include -#include // u/intmax_t - -/** - * @brief Begin a loop over a range of integer values. Supports: - * - * - `mlib_foreach_{u,i}range(Var, Stop)` - * - `mlib_foreach_{u,i}range(Var, Start, Stop)` - * - * If omitted, starts at zero. The loop does not include the `Stop` value. The `Var` - * variable cannot be modified within the loop. The loop variable is declared as the maximum - * precision type for the requested signedness. - */ -#define mlib_foreach_urange(...) MLIB_ARGC_PICK(_mlib_foreach_urange, __VA_ARGS__) -#define mlib_foreach_irange(...) MLIB_ARGC_PICK(_mlib_foreach_irange, __VA_ARGS__) -#define _mlib_foreach_urange_argc_2(VarName, Stop) _mlib_foreach_urange_argc_3(VarName, 0, Stop) -#define _mlib_foreach_urange_argc_3(VarName, Start, Stop) \ - _mlibForeachRange(uintmax_t, \ - VarName, \ - Start, \ - Stop, \ - MLIB_PASTE(VarName, _start), \ - MLIB_PASTE(VarName, _stop), \ - MLIB_PASTE(VarName, _counter)) -#define _mlib_foreach_irange_argc_2(VarName, Stop) _mlib_foreach_irange_argc_3(VarName, 0, Stop) -#define _mlib_foreach_irange_argc_3(VarName, Start, Stop) \ - _mlibForeachRange(intmax_t, \ - VarName, \ - Start, \ - Stop, \ - MLIB_PASTE(VarName, _start), \ - MLIB_PASTE(VarName, _stop), \ - MLIB_PASTE(VarName, _counter)) - -/** - * @brief Loop over a pointed-to array - * - * @param T The type of the array elements - * @param Var Identifier to declare as the pointer to the current element - * @param ArrayPtr A pointer to the beginning of the array - * @param Count The number of elements in the array - */ -#define mlib_foreach(T, Var, ArrayPtr, Count) \ - _mlibForeach(T, Var, ArrayPtr, Count, MLIB_PASTE(Var, _start), MLIB_PASTE(Var, _stop), MLIB_PASTE(Var, _iter)) -/** - * @brief Loop over the elements of a C array - * - * @param T the type of the array elements - * @param Var Identifier to declare as the pointer to the current element - * @param Array An expression of array type (not a pointer) - */ -#define mlib_foreach_arr(T, Var, Array) mlib_foreach (T, Var, Array, (sizeof Array / sizeof Array[0])) - -// clang-format off -#define _mlibForeachRange(VarType, VarName, StartValue, StopValue, StartVar, StopVar, Counter) \ - _mlibLoopMagicBegin() \ - /* Capture the starting and stopping value first */ \ - for (VarType StartVar = (StartValue), StopVar = (StopValue); !_mlibLoopIsDone;) \ - _mlibLoopMagicEnd( \ - /* Init counter to the start value */ \ - VarType Counter = StartVar, \ - /* Stop when the counter is not less than the stop value */ \ - (_mlibLoopState.first = Counter == StartVar, \ - _mlibLoopState.last = Counter + 1 == StopVar, \ - Counter < StopVar), \ - /* Increment the counter at loop end */ \ - ++Counter, \ - /* Declare the loop variable as const at the start of each iteraiton */ \ - const VarType VarName = Counter) - -#define _mlibForeach(T, VarName, ArrayPtr, Count, StartVar, StopVar, Iter) \ - _mlibLoopMagicBegin() \ - /* Capture the starting and stopping position so we only evaluate them once */ \ - for (T* const StartVar = (ArrayPtr) + 0; !_mlibLoopIsDone;) \ - for (T* const StopVar = StartVar + (Count); !_mlibLoopIsDone;) \ - _mlibLoopMagicEnd( \ - /* Init the iteration pointer to the array start */ \ - T* Iter = StartVar, \ - /* Stop when the iterator points to the stop position */ \ - (_mlibLoopState.first = Iter == StartVar, \ - _mlibLoopState.last = Iter + 1 == StopVar, \ - Iter != StopVar), \ - /* Advance the iterator on each loop */ \ - ++Iter, \ - /* Declare a constant pointer to the current element at the top of the loop */ \ - T* const VarName = Iter) - - -#define _mlibLoopDidBreak MLIB_PASTE(_mlibLoopDidBreak_lno_, __LINE__) -#define _mlibLoopOnce MLIB_PASTE(_mlibLoopOnce_lno_, __LINE__) -#define _mlibLoopIsDone MLIB_PASTE(_mlibLoopIsDone_lno_, __LINE__) -#define _mlibLoopIsState MLIB_PASTE(_mlibLoopIsState_lno_, __LINE__) -#define _mlibLoopMagicBegin() \ - /* Loop stop condition */ \ - for (int _mlibLoopIsDone = 0; !_mlibLoopIsDone;) \ - /* Track if the user broke out of the inner loop */ \ - for (int _mlibLoopDidBreak = 0; !_mlibLoopIsDone;) \ - /* Loop variables */ \ - for (struct mlib_loop_state _mlibLoopState = {0, 0, 0}; !_mlibLoopIsDone;) - -/** - * @brief Struct type declared within the scope of an `mlib_foreach` loop, which - * contains information about the running loop. - */ -struct mlib_loop_state { - // The current zero-based index of the loop - size_t index; - // Whether the current iteration is the first in the loop - bool first; - // Whether the current iteration will be the last in the loop - bool last; -}; - -/// InitStmt: Statement that executes once at the top of the loop -/// ContinueCond: Condition at which the loop will stop -/// StepExpr: Expression for the loop step -/// HeadStmt: A statement that appears at the head of the loop, executed once on each iteration -#define _mlibLoopMagicEnd(InitStmt, ContinueCond, StepExpr, HeadStmt) \ - for (\ - /* Run the init statement */ \ - InitStmt; \ - /* Test the loop condition, unless we `break` out of the loop */ \ - !(_mlibLoopIsDone = _mlibLoopIsDone || !(ContinueCond)); \ - /* Run the step expression, unless we `break` from the loop */ \ - (void)(_mlibLoopIsDone || ((void)(StepExpr), 1))) \ - /* `break` detection: */ \ - for (int _mlibLoopOnce = 0; !_mlibLoopOnce; _mlibLoopOnce = 1, _mlibLoopIsDone = _mlibLoopDidBreak) \ - /* Loop state information */ \ - for (const struct mlib_loop_state loop = _mlibLoopState; \ - !_mlibLoopOnce; \ - ((void)loop, _mlibLoopOnce = 1, ++_mlibLoopState.index)) \ - for (HeadStmt; \ - /* Set `_mlibLoopDidBreak` to true at the start of the loop: */ \ - !_mlibLoopOnce && (_mlibLoopDidBreak = 1); \ - /* If loop exits normally, set `_mlibLoopDidBreak` to false */ \ - _mlibLoopDidBreak = 0, _mlibLoopOnce = 1) - - -// clang-format on - -#endif // MLIB_LOOP_H_INCLUDED diff --git a/bsonjs/common/mlib/platform.h b/bsonjs/common/mlib/platform.h deleted file mode 100644 index 8809575..0000000 --- a/bsonjs/common/mlib/platform.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @file mlib/platform.h - * @brief Operating System Headers and Definitions - * @date 2025-04-21 - * - * This file will conditionally include the general system headers available - * for the current host platform. - * - * @copyright Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MLIB_PLATFORM_H_INCLUDED -#define MLIB_PLATFORM_H_INCLUDED - -// clang-format off - -// Windows headers -#ifdef _WIN32 - // Check that our WINNT version isn't too old to be used - #if defined(_WIN32_WINNT) && (_WIN32_WINNT < 0x601) - #undef _WIN32_WINNT - #endif - #ifndef _WIN32_WINNT - // Request a new-enough version of the Win32 API (required for MinGW) - #define _WIN32_WINNT 0x601 - #endif - // Winsock must be included before windows.h - #include // IWYU pragma: export - #include // IWYU pragma: export -#endif - -// POSIX headers -#if defined(__unix__) || defined(__unix) || defined(__APPLE__) - #include // IWYU pragma: export - #include // IWYU pragma: export - #include // IWYU pragma: export -#endif - -// clang-format on - -#endif // MLIB_PLATFORM_H_INCLUDED diff --git a/bsonjs/common/mlib/str.h b/bsonjs/common/mlib/str.h deleted file mode 100644 index 795ad7c..0000000 --- a/bsonjs/common/mlib/str.h +++ /dev/null @@ -1,1239 +0,0 @@ -/** - * @file mlib/str.h - * @brief String handling utilities - * @date 2025-04-30 - * - * This file provides utilities for handling *sized* strings. That is, strings - * that carry their size, and do not rely on null termination. These APIs also - * do a lot more bounds checking than is found in ``. - * - * @copyright Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MLIB_STR_H_INCLUDED -#define MLIB_STR_H_INCLUDED - -#include -#include -#include -#include -#include -#include - -#include // va_list -#include -#include -#include -#include // vsnprintf -#include // malloc/free -#include // memcpy - -/** - * @brief A simple non-owning string-view type. - * - * The viewed string can be treated as an array of `char`. It's pointed-to data - * must not be freed or manipulated. - * - * @note The viewed string is NOT guaranteed to be null-terminated. It WILL - * be null-terminated if: Directly created from a string literal, a C string, or - * a null-terminated `mstr_view`. - * @note The viewed string MAY contain nul (zero-value) characters, so using them - * with C string APIs could truncate unexpectedly. - * @note The view itself may be "null" if the `data` member of the string view - * is a null pointer. A zero-initialized `mstr_view` is null. - */ -typedef struct mstr_view { - /** - * @brief Pointer to the string data viewed by this object. - * - * - This pointer may be null, in which case the string view itself is "null". - * - If `len > 1`, then this points to a contiguous array of `char` of length - * `len`. - * - If `len == 1`, then this *may* point to a single `char` object. - * - The pointed-to string might not be a null-terminated C string. Accessing - * the `char` value at `data[len]` is undefined behavior. - */ - const char *data; - /** - * @brief The length of the viewed string pointed-to by `data` - * - * If `data` points to a single `char` object, then this must be `1`. If - * `data` is a null pointer, then this value should be zero. - */ - size_t len; -} mstr_view; - -/** - * @brief Expand to the two printf format arguments required to format an mstr object - * - * You should use the format specifier `%.*s' for all mstr strings. - * - * This is just a convenience shorthand. - */ -#define MSTR_FMT(S) (int)mstr_view_from(S).len, mstr_view_from(S).data - -/** - * @brief Create an `mstr_view` that views the given array of `char` - * - * @param data Pointer to the beginning of the string, or pointer to a single - * `char`, or a null pointer - * @param len Length of the new string-view. If `data` points to a single `char`, - * this must be `0` or `1`. If `data` is a null pointer, this should be `0`. - * - * @note This is defined as a macro that expands to a compound literal to prevent - * proliferation of redundant function calls in debug builds. - */ -#define mstr_view_data(DataPointer, Length) (mlib_init(mstr_view){(DataPointer), (Length)}) - -#if 1 // See "!! NOTE" below - -/** - * @brief Coerce a string-like object to an `mstr_view` of that string - * - * This macro requires that the object have `.data` and `.len` members. - * - * @note This macro will double-evaluate its argument. - */ -#define mstr_view_from(X) mstr_view_data((X).data, (X).len) - -/** - * ! NOTE: The disabled snippet below is kept for posterity as a drop-in replacment - * ! for mstr_view_from with support for _Generic. - * - * When we can increase the compiler requirements to support _Generic, the following - * macro definition alone makes almost every function in this file significantly - * more concise to use, as it allows us to pass a C string to any API that - * expects an `mstr_view`, enabling code like this: - * - * ``` - * mstr s = get_string(); - * if (mstr_cmp(s, ==, "magicKeyword")) { - * Do something... - * } - * ``` - * - * This also allows us to avoid the double-evaluation problem presented by - * `mstr_view_from` being defined as above. - * - * Without _Generic, we require all C strings to be wrapped with `mstr_cstring`, - * which isn't especially onerous, but it is annoying. Additionally, the below - * `_Generic` macro can be extended to support more complex string-like types. - * - * For reference, support for _Generic requires the following compilers: - * - * - MSVC 19.28.0+ (VS 2019, 16.8.1) - * - GCC 4.9+ - * - Clang 3.0+ - */ - -#else - -/** - * @brief Coerce an object to an `mstr_view` - * - * The object requires a `data` and `len` member - */ -#define mstr_view_from(X) \ - _Generic((X), mstr_view: _mstr_view_trivial_copy, char *: mstr_cstring, const char *: mstr_cstring)((X)) -// Just copy an mstr_view by-value -static inline mstr_view -_mstr_view_trivial_copy(mstr_view s) -{ - return s; -} - -#endif - - -/** - * @brief Create an `mstr_view` referring to the given null-terminated C string - * - * @param s Pointer to a C string. The length of the returned string is infered using `strlen` - * - * This should not defined as a macro, because defining it as a macro would require - * double-evaluating for the call to `strlen`. - */ -static inline mstr_view -mstr_cstring(const char *s) -{ - const size_t l = strlen(s); - return mstr_view_data(s, l); -} - -/** - * @brief Compare two strings lexicographically by each code unit - * - * If called with two arguments behaves the same as `strcmp`. If called with - * three arguments, the center argument should be an infix operator to perform - * the semantic comparison. - */ -static inline enum mlib_cmp_result -mstr_cmp(mstr_view a, mstr_view b) -{ - size_t l = a.len; - if (b.len < l) { - l = b.len; - } - // Use `memcmp`, not `strncmp`: We want to respect nul characters - int r = memcmp(a.data, b.data, l); - if (r) { - // Not equal: Compare with zero to normalize to the cmp_result value - return mlib_cmp(r, 0); - } - // Same prefixes, the ordering is now based on their length (longer string > shorter string) - return mlib_cmp(a.len, b.len); -} - -#define mstr_cmp(...) MLIB_ARGC_PICK(_mstr_cmp, __VA_ARGS__) -#define _mstr_cmp_argc_2(A, B) mstr_cmp(mstr_view_from(A), mstr_view_from(B)) -#define _mstr_cmp_argc_3(A, Op, B) (_mstr_cmp_argc_2(A, B) Op 0) - -/** - * @brief If the given codepoint is a Basic Latin (ASCII) uppercase character, - * return the lowercase character. Other codepoint values are returned unchanged. - * - * This is safer than `tolower`, because it doesn't respect locale and has no - * undefined behavior. - */ -static inline int32_t -mlib_latin_tolower(int32_t a) -{ - if (a >= 0x41 /* "A" */ && a <= 0x5a /* "Z" */) { - a += 0x20; // Adjust from "A" -> "a" - } - return a; -} - -/** - * @brief Compare two individual codepoint values, with case-insensitivity in - * the Basic Latin range. - */ -static inline enum mlib_cmp_result -mlib_latin_charcasecmp(int32_t a, int32_t b) -{ - return mlib_cmp(mlib_latin_tolower(a), mlib_latin_tolower(b)); -} - -/** - * @brief Compare two strings lexicographically, case-insensitive in the Basic - * Latin range. - * - * If called with two arguments, behaves the same as `strcasecmp`. If called with - * three arguments, the center argument should be an infix operator to perform - * the semantic comparison. - */ -static inline enum mlib_cmp_result -mstr_latin_casecmp(mstr_view a, mstr_view b) -{ - size_t l = a.len; - if (b.len < l) { - l = b.len; - } - mlib_foreach_urange (i, l) { - // We don't need to do any UTF-8 decoding, because our case insensitivity - // only activates for 1-byte encoded codepoints, and all other valid UTF-8 - // sequences will collate equivalently with byte-wise comparison to a UTF-32 - // encoding. - enum mlib_cmp_result r = mlib_latin_charcasecmp(a.data[i], b.data[i]); - if (r) { - // Not equivalent at this code unit. Return this as the overall string ordering. - return r; - } - } - // Same prefixes, the ordering is now based on their length (longer string > shorter string) - return mlib_cmp(a.len, b.len); -} - -#define mstr_latin_casecmp(...) MLIB_ARGC_PICK(_mstr_latin_casecmp, __VA_ARGS__) -#define _mstr_latin_casecmp_argc_2(A, B) mstr_latin_casecmp(mstr_view_from(A), mstr_view_from(B)) -#define _mstr_latin_casecmp_argc_3(A, Op, B) (_mstr_latin_casecmp_argc_2(A, B) Op 0) - -/** - * @brief Adjust a possibly negative index position to wrap around for a string - * - * @param s The string to be respected for index wrapping - * @param pos The maybe-negative index to be adjusted - * @param clamp_to_length If `true` and given a non-negative value, if that - * value is greater than the string length, this function will return the string - * length instead. - * @return size_t The new zero-based non-negative index - * - * If `pos` is negative, then it represents indexing from the end of the string, - * where `-1` refers to the last character in the string, `-2` the penultimate, - * etc. If the absolute value is greater than the length of the string, the - * program will be terminated. - */ -static inline size_t -_mstr_adjust_index(mstr_view s, mlib_upsized_integer pos, bool clamp_to_length) -{ - if (clamp_to_length && (mlib_cmp)(pos, mlib_upsize_integer(s.len), 0) == mlib_greater) { - // We want to clamp to the length, and the given value is greater than the string length. - return s.len; - } - if (pos.is_signed && pos.bits.as_signed < 0) { - // This will add the negative value to the length of the string. If such - // an operation would result a negative value, this will terminate the - // program. - return mlib_assert_add(size_t, s.len, pos.bits.as_signed); - } - // No special behavior, just assert that the given position is in-bounds for the string - mlib_check( - pos.bits.as_unsigned <= s.len, because, "the string position index must not be larger than the string length"); - return pos.bits.as_unsigned; -} - -/** - * @brief Obtain the code unit at the given zero-based index, with negative index wrapping. - * - * This function asserts that the index is in-bounds for the given string. - * - * @param s The string to be inspected. - * @param pos The index to access. Zero is the first code unit, and -1 is the last. - * @return char The code unit at position `pos`. - */ -static inline char -mstr_at(mstr_view s, mlib_upsized_integer pos_) -{ - size_t pos = _mstr_adjust_index(s, pos_, false); - return s.data[pos]; -} - -#define mstr_at(S, Pos) (mstr_at)(mstr_view_from(S), mlib_upsize_integer(Pos)) - -/** - * @brief Create a new `mstr_view` that views a substring within another string - * - * @param s The original string view to be inspected - * @param pos The number of `char` to skip in `s`, or a negative value to - * pos from the end of the string. - * @param len The length of the new string view (optional, default SIZE_MAX) - * - * The length of the string view is clamped to the characters available in `s`, - * so passing a too-large value for `len` is well-defined. Passing a too-large - * value for `pos` will abort the program. - * - * Callable as: - * - * - `mstr_substr(s, pos)` - * - `mstr_substr(s, pos, len)` - */ -static inline mstr_view -mstr_substr(mstr_view s, mlib_upsized_integer pos_, size_t len) -{ - const size_t pos = _mstr_adjust_index(s, pos_, false); - // Number of characters in the string after we remove the prefix - const size_t remain = s.len - pos; - // Clamp the new length to the size that is actually available. - if (len > remain) { - len = remain; - } - return mstr_view_data(s.data + pos, len); -} - -#define mstr_substr(...) MLIB_ARGC_PICK(_mstr_substr, __VA_ARGS__) -#define _mstr_substr_argc_2(Str, Start) _mstr_substr_argc_3(Str, Start, SIZE_MAX) -#define _mstr_substr_argc_3(Str, Start, Stop) mstr_substr(mstr_view_from(Str), mlib_upsize_integer(Start), Stop) - -/** - * @brief Obtain a slice of the given string view, where the two arguments are zero-based indices into the string - * - * @param s The string to be sliced - * @param start The zero-based index of the new string start - * @param end The zero-based index of the first character to exclude from the new string - * - * @note Unlike `substr`, the second argument is required, and must specify the index at which the - * string will end, rather than the length of the string. - */ -static inline mstr_view -mstr_slice(const mstr_view s, const mlib_upsized_integer start_, const mlib_upsized_integer end_) -{ - const size_t start_pos = _mstr_adjust_index(s, start_, false); - const size_t end_pos = _mstr_adjust_index(s, end_, true); - mlib_check(end_pos >= start_pos, because, "Slice positions must end after the start position"); - const size_t sz = (size_t)(end_pos - start_pos); - return mstr_substr(s, start_pos, sz); -} -#define mstr_slice(S, StartPos, EndPos) \ - mstr_slice(mstr_view_from(S), mlib_upsize_integer((StartPos)), mlib_upsize_integer((EndPos))) - -/** - * @brief Find the first occurrence of `needle` within `hay`, returning the zero-based index - * if found, and `SIZE_MAX` if it is not found. - * - * @param hay The string which is being scanned - * @param needle The substring that we are searching to find - * @param pos The start position of the search (optional, default zero) - * @param len The number of characters to search in `hay` (optional, default SIZE_MAX) - * @return size_t If found, the zero-based index of the first occurrence within - * the string. If not found, returns `SIZE_MAX`. - * - * The `len` is clamped to the available string length. - * - * Callable as: - * - * - `mstr_find(hay, needle)` - * - `mstr_find(hay, needle, pos)` - * - `mstr_find(hay, needle, pos, len)` - */ -static inline size_t -mstr_find(mstr_view hay, mstr_view const needle, mlib_upsized_integer const pos_, size_t const len) -{ - const size_t pos = _mstr_adjust_index(hay, pos_, false); - // Trim the hay according to our search window: - hay = mstr_substr(hay, pos, len); - - // Larger needle can never exist within the smaller string: - if (hay.len < needle.len) { - return SIZE_MAX; - } - - // Set the index at which we can stop searching early. This will never - // overflow, because we guard against hay.len > needle.len - size_t stop_idx = hay.len - needle.len; - // Use "<=", because we do want to include the final search position - for (size_t offset = 0; offset <= stop_idx; ++offset) { - if (memcmp(hay.data + offset, needle.data, needle.len) == 0) { - // Return the found position. Adjust by the start pos since we may - // have trimmed the search window - return offset + pos; - } - } - - // Nothing was found. Return SIZE_MAX to indicate the not-found - return SIZE_MAX; -} - -#define mstr_find(...) MLIB_ARGC_PICK(_mstr_find, __VA_ARGS__) -#define _mstr_find_argc_2(Hay, Needle) _mstr_find_argc_3(Hay, Needle, 0) -#define _mstr_find_argc_3(Hay, Needle, Start) _mstr_find_argc_4(Hay, Needle, Start, SIZE_MAX) -#define _mstr_find_argc_4(Hay, Needle, Start, Stop) \ - mstr_find(mstr_view_from(Hay), mstr_view_from(Needle), mlib_upsize_integer(Start), Stop) - -/** - * @brief Find the zero-based index of the first `char` in `hay` that also occurs in `needles` - * - * This is different from `find()` because it considers each char in `needles` as an individual - * one-character string to be search for in `hay`. - * - * @param hay The string to be searched - * @param needles A string containing a set of characters which are searched for in `hay` - * @param pos The index at which to begin searching (optional, default is zero) - * @param len The number of characters in `hay` to consider before stopping (optional, default is SIZE_MAX) - * @return size_t If a needle is found, returns the zero-based index of that first needle. - * Otherwise, returns SIZE_MAX. - * - * Callable as: - * - * - `mstr_find_first_of(hay, needles)` - * - `mstr_find_first_of(hay, needles, pos)` - * - `mstr_find_first_of(hay, needles, pos, len)` - */ -static inline size_t -mstr_find_first_of(mstr_view hay, mstr_view const needles, mlib_upsized_integer const pos_, size_t const len) -{ - const size_t pos = _mstr_adjust_index(hay, pos_, false); - // Trim to fit the search window - hay = mstr_substr(hay, pos, len); - // We search by incrementing an index - mlib_foreach_urange (idx, hay.len) { - // Grab a substring of the single char at the current search index - mstr_view one = mstr_substr(hay, idx, 1); - // Test if the single char occurs anywhere in the needle set - if (mstr_find(needles, one) != SIZE_MAX) { - // We found the first index in `hay` where one of the needles occurs. Adjust - // by `pos` since we may have trimmed - return idx + pos; - } - } - return SIZE_MAX; -} - -#define mstr_find_first_of(...) MLIB_ARGC_PICK(_mstr_find_first_of, __VA_ARGS__) -#define _mstr_find_first_of_argc_2(Hay, Needle) _mstr_find_first_of_argc_3(Hay, Needle, 0) -#define _mstr_find_first_of_argc_3(Hay, Needle, Pos) _mstr_find_first_of_argc_4(Hay, Needle, Pos, SIZE_MAX) -#define _mstr_find_first_of_argc_4(Hay, Needle, Pos, Len) mstr_find_first_of(Hay, Needle, mlib_upsize_integer(Pos), Len) - -/** - * @brief Test whether the given codepoint is a Basic Latin whitespace character - * - * This function does not depend on the locale and has no undefined behavior, unlike functions - * - * @param c The codepoint to be tested - */ -static inline bool -mlib_is_latin_whitespace(int32_t c) -{ - switch (c) { - case 0x09: // horizontal tab - case 0x0a: // line feed - case 0x0d: // carriage return - case 0x20: // space - return true; - - default: - return false; - } -} - -/** - * @brief Trim leading latin (ASCII) whitespace from the given string - * - * @param s The string to be inspected - * @return mstr_view A substring view of `s` that excludes any leading whitespace - */ -static inline mstr_view -mstr_trim_left(mstr_view s) -{ - // Testing arbitrary code units for whitespace is safe as only 1-byte-encoded - // codepoints can land within the Basic Latin range: - while (s.len && mlib_is_latin_whitespace(mstr_at(s, 0))) { - s = mstr_substr(s, 1); - } - return s; -} -#define mstr_trim_left(S) (mstr_trim_left)(mstr_view_from(S)) - -/** - * @brief Trim trailing latin (ASCII) whitespace from the given string - * - * @param s The string to be insepcted - * @return mstr_view A substring view of `s` that excludes any trailing whitespace. - */ -static inline mstr_view -mstr_trim_right(mstr_view s) -{ - while (s.len && mlib_is_latin_whitespace(mstr_at(s, -1))) { - s = mstr_slice(s, 0, -1); - } - return s; -} -#define mstr_trim_right(S) (mstr_trim_right)(mstr_view_from(S)) - -/** - * @brief Trim leading and trailing latin (ASCII) whitespace from the string - * - * @param s The string to be inspected - * @return mstr_view A substring of `s` that excludes leading and trailing whitespace. - */ -static inline mstr_view -mstr_trim(mstr_view s) -{ - s = mstr_trim_left(s); - s = mstr_trim_right(s); - return s; -} -#define mstr_trim(S) (mstr_trim)(mstr_view_from(S)) - -/** - * @brief Split a single string view into two strings at the given position - * - * @param s The string to be split - * @param pos The position at which the prefix string is ended - * @param drop [optional] The number of characters to drop between the prefix and suffix - * @param prefix [out] Updated to point to the part of the string before the split - * @param suffix [out] Updated to point to the part of the string after the split - * - * `pos` and `drop` are clamped to the size of the input string. - * - * Callable as: - * - * - `mstr_split_at(s, pos, prefix, suffix)` - * - `mstr_split_at(s, pos, drop, prefix, suffix)` - * - * If either `prefix` or `suffix` is a null pointer, then they will be ignored - */ -static inline void -mstr_split_at(mstr_view s, mlib_upsized_integer pos_, size_t drop, mstr_view *prefix, mstr_view *suffix) -{ - const size_t pos = _mstr_adjust_index(s, pos_, true /* clamp to the string size */); - // Save the prefix string - if (prefix) { - *prefix = mstr_substr(s, 0, pos); - } - // Save the suffix string - if (suffix) { - // The number of characters that remain after the prefix is removed - const size_t remain = s.len - pos; - // Clamp the number of chars to drop to not overrun the input string - if (remain < drop) { - drop = remain; - } - // The start position of the new string - const size_t next_start = pos + drop; - *suffix = mstr_substr(s, next_start, SIZE_MAX); - } -} - -#define mstr_split_at(...) MLIB_ARGC_PICK(_mstr_split_at, __VA_ARGS__) -#define _mstr_split_at_argc_4(Str, Pos, Prefix, Suffix) _mstr_split_at_argc_5(Str, Pos, 0, Prefix, Suffix) -#define _mstr_split_at_argc_5(Str, Pos, Drop, Prefix, Suffix) \ - mstr_split_at(mstr_view_from(Str), mlib_upsize_integer(Pos), Drop, Prefix, Suffix) - -/** - * @brief Split a string in two around the first occurrence of some infix string. - * - * @param s The string to be split in twain - * @param infix The infix string to be searched for - * @param prefix The part of the string that precedes the infix (nullable) - * @param suffix The part of the string that follows the infix (nullable) - * @return true If the infix was found - * @return false Otherwise - * - * @note If `infix` does not occur in `s`, then `*prefix` will be set equal to `s`, - * and `*suffix` will be made an empty string, as if the infix occurred at the end - * of the string. - */ -static inline bool -mstr_split_around(mstr_view s, mstr_view infix, mstr_view *prefix, mstr_view *suffix) -{ - // Find the position of the infix. If it is not found, returns SIZE_MAX - const size_t pos = mstr_find(s, infix); - // Split at the infix, dropping as many characters as are in the infix. If - // the `pos` is SIZE_MAX, then this call will clamp to the end of the string. - mstr_split_at(s, pos, infix.len, prefix, suffix); - // Return `true` if we found the infix, indicated by a not-SIZE_MAX `pos` - return pos != SIZE_MAX; -} - -#define mstr_split_around(Str, Infix, PrefixPtr, SuffixPtr) \ - mstr_split_around(mstr_view_from((Str)), mstr_view_from((Infix)), (PrefixPtr), (SuffixPtr)) - -/** - * @brief Test whether the given string starts with the given prefix - * - * @param str The string to be tested - * @param prefix The prefix to be searched for - * @return true if-and-only-if `str` starts with `prefix` - * @return false Otherwise - */ -static inline bool -mstr_starts_with(mstr_view str, mstr_view prefix) -{ - // Trim to match the length of the prefix we want - str = mstr_substr(str, 0, prefix.len); - // Check if the trimmed string is the same as the prefix - return mstr_cmp(str, ==, prefix); -} -#define mstr_starts_with(Str, Prefix) mstr_starts_with(mstr_view_from(Str), mstr_view_from(Prefix)) - -/** - * @brief Test whether a substring occurs at any point within the given string - * - * @param str The string to be inspected - * @param needle The substring to be searched for - * @return true If-and-only-if `str` contains `needle` at any position - * @return false Otherise - */ -static inline bool -mstr_contains(mstr_view str, mstr_view needle) -{ - return mstr_find(str, needle) != SIZE_MAX; -} -#define mstr_contains(Str, Needle) mstr_contains(mstr_view_from(Str), mstr_view_from(Needle)) - -/** - * @brief Test whether a given string contains any of the characters in some other string - * - * @param str The string to be inspected - * @param needle A string to be treated as a set of one-byte characters to search for - * @return true If-and-only-if `str` contains `needle` at any position - * @return false Otherise - * - * @note This function does not currently support multi-byte codepoints - */ -static inline bool -mstr_contains_any_of(mstr_view str, mstr_view needle) -{ - return mstr_find_first_of(str, needle) != SIZE_MAX; -} -#define mstr_contains_any_of(Str, Needle) mstr_contains_any_of(mstr_view_from(Str), mstr_view_from(Needle)) - - -/** - * @brief A simple mutable string type, with a guaranteed null terminator. - * - * This type is a trivially relocatable aggregate type that contains a pointer `data` - * and a size `len`. If not null, the pointer `data` points to an array of mutable - * `char` of length `len + 1`, where the character at `data[len]` is always zero, - * and must not be modified. - * - * @note The string MAY contain nul (zero-value) characters, so using them with - * C string APIs could truncate unexpectedly. - * @note The string itself may be "null" if the `data` member of the string is - * a null pointer. A zero-initialized `mstr` is null. The null string is distinct - * from the empty string, which has a non-null `.data` that points to an empty - * C string. - */ -typedef struct mstr { - /** - * @brief Pointer to the first char in the string, or NULL if - * the string is null. - * - * The pointed-to character array has a length of `len + 1`, where - * the character at `data[len]` is always null. - * - * @warning Attempting to overwrite the null character at `data[len]` - * will result in undefined behavior! - * - * @note An empty string is not equivalent to a null string! An empty string - * will still point to an array of length 1, where the only char is the null - * terminator. - */ - char *data; - /** - * @brief The number of characters in the array pointed-to by `data` - * that precede the null terminator. - */ - size_t len; -} mstr; - - -/** - * @brief Resize an existing or null `mstr`, without initializing any of the - * added content other than the null terminator. This operation is potentially - * UNSAFE, because it gives uninitialized memory to the caller. - * - * @param str Pointer to a valid `mstr`, or a null `mstr`. - * @param new_len The new length of the string. - * @return true If the operation succeeds - * @return false Otherwise - * - * If `str` is a null string, this function will initialize a new `mstr` object - * on-the-fly. - * - * If the operation increases the length of the string (or initializes a new string), - * then the new `char` in `str.data[str.len : new_len] will contain uninitialized - * values. The char at `str.data[new_len]` WILL be set to zero, to ensure there - * is a null terminator. The caller should always initialize the new string - * content to ensure that the string has a specified value. - */ -static inline bool -mstr_resize_for_overwrite(mstr *const str, const size_t new_len) -{ - // We need to allocate one additional char to hold the null terminator - size_t alloc_size = new_len; - if (mlib_unlikely(mlib_add(&alloc_size, 1) || alloc_size > PTRDIFF_MAX)) { - // Allocation size is too large - return false; - } - // Try to (re)allocate the region - char *data = (char *)realloc(str->data, alloc_size); - if (!data) { - // Failed to (re)allocate - return false; - } - // Note: We do not initialize any of the data in the newly allocated region. - // We only set the null terminator. It is up to the caller to do the rest of - // the init. - data[new_len] = '\0'; - // Update the final object - str->data = data; - str->len = new_len; - // Success - return true; -} - -/** - * @brief Given an existing `mstr`, resize it to hold `new_len` chars - * - * @param str Pointer to a string object to update, or a null `mstr` - * @param new_len The new length of the string, not including the implicit null terminator - * @return true If the operation succeeds - * @return false Otherwise - * - * @note If the operation fails, then `*str` is not modified. - */ -static inline bool -mstr_resize(mstr *str, size_t new_len) -{ - const size_t old_len = str->len; - if (!mstr_resize_for_overwrite(str, new_len)) { - // Failed to allocate new storage for the string - return false; - } - // Check how many chars we added/removed - const ptrdiff_t len_diff = mlib_assert_sub(ptrdiff_t, new_len, str->len); - if (len_diff > 0) { - // We added new chars. Zero-init all the new chars - memset(str->data + old_len, 0, (size_t)len_diff); - } - // Success - return true; -} - -/** - * @brief Create a new `mstr` of the given length - * - * @param new_len The length of the new string, in characters, not including the null terminator - * @return mstr A new string. The string's `data` member is NULL in case of failure - * - * The character array allocated for the string will always be `new_len + 1` `char` in length, - * where the char at the index `new_len` is a null terminator. This means that a string of - * length zero will allocate a single character to store the null terminator. - * - * All characters in the new string are initialize to zero. If you want uninitialized - * string content, use `mstr_resize_for_overwrite`. - */ -static inline mstr -mstr_new(size_t new_len) -{ - mstr ret = {NULL, 0}; - // We can rely on `resize` to handle the null state properly. - mstr_resize(&ret, new_len); - return ret; -} - -/** - * @brief Free the resources associated with an mstr object. - * - * @param s Pointer to an `mstr` object. If pointer or the pointed-to-object is null, - * this function is a no-op. - * - * After this call, the pointed-to `s` will be a null `mstr` - */ -static inline void -mstr_destroy(mstr *s) -{ - if (s) { - free(s->data); - s->len = 0; - s->data = NULL; - } -} - -/** - * @brief Obtain a null mstr string object. - * - * @return mstr A null string, with a null data pointer and zero size - */ -static inline mstr -mstr_null(void) -{ - return mlib_init(mstr){0}; -} - -/** - * @internal - * @brief Test whether the given string-view is a view within the given owning string - */ -static inline bool -_mstr_overlaps(mstr const *str, mstr_view sv) -{ - // Note: Pointer-comparison between objects is unspecified, but is guaranteed - // to returns `true` if there is overlap. We're okay with false-positive overlaps. - // Additionally, POSIX and Win32 both offer stronger guarantees about pointer - // comparison, which we can rely on here. - return str->data // - && str->data <= sv.data // - && sv.data <= str->data + str->len; -} - -/** - * @brief Replace the content of the given string, attempting to reuse the buffer - * - * @param inout Pointer to a valid or null `mstr` to be replaced - * @param s The new string contents - * @return true If the operation succeeded - * @return false Otherwise - * - * If the operation fails, `*inout` is not modified - */ -static inline bool -mstr_assign(mstr *inout, mstr_view s) -{ - // Check for self-assignment - if (_mstr_overlaps(inout, s)) { - // We are overwriting a string with a (sub)string of its own content. - // Move the substring to the front of the string (may be a no-op if `s` - // points to the beginning of the string) - memmove(inout->data, s.data, s.len); - // Resize to truncate. This will always shrink the string, because a valid - // string-view into `inout` cannot be longer than `inout` itself. Thus, it - // also cannot fail. - mstr_resize_for_overwrite(inout, s.len); - return true; - } - if (!mstr_resize_for_overwrite(inout, s.len)) { - return false; - } - memcpy(inout->data, s.data, s.len); - return true; -} - -#define mstr_assign(InOut, S) mstr_assign((InOut), mstr_view_from((S))) - -/** - * @brief Create a mutable copy of the given string. - * - * @param sv The string to be copied - * @return mstr A new valid string, or a null string in case of allocation failure. - */ -static inline mstr -mstr_copy(mstr_view sv) -{ - mstr ret = {NULL, 0}; - mstr_assign(&ret, sv); - return ret; -} - -#define mstr_copy(S) mstr_copy(mstr_view_from((S))) -#define mstr_copy_cstring(S) mstr_copy(mstr_cstring((S))) - -/** - * @brief Concatenate two strings into a new mutable string - * - * @param a The left-hand string to be concatenated - * @param b The right-hand string to be concatenated - * @return mstr A new valid string composed by concatenating `a` with `b`, or - * a null string in case of allocation failure. - */ -static inline mstr -mstr_concat(mstr_view a, mstr_view b) -{ - mstr ret = {NULL, 0}; - size_t cat_len = 0; - if (mlib_unlikely(mlib_add(&cat_len, a.len, b.len))) { - // Size would overflow. No go. - return ret; - } - // Prepare the new string - if (!mstr_resize_for_overwrite(&ret, cat_len)) { - // Failed to allocate. The ret string is still null, and we can just return it - return ret; - } - // Copy in the characters from `a` - char *out = ret.data; - memcpy(out, a.data, a.len); - // Copy in the characters from `b` - out += a.len; - memcpy(out, b.data, b.len); - // Success - return ret; -} - -#define mstr_concat(A, B) mstr_concat(mstr_view_from((A)), mstr_view_from((B))) - -/** - * @brief Delete and/or insert characters into a string - * - * @param str The string object to be updated - * @param splice_pos The position at which to do the splice - * @param n_delete The number of characters to delete at `splice_pos` - * @param insert A string to be inserted at `split_pos` after chars are deleted - * @return true If the operation succeeds - * @return false Otherwise - * - * If `n_delete` is zero, then no characters are deleted. If `insert` is empty - * or null, then no characters are inserted. - */ -static inline bool -mstr_splice(mstr *str, size_t splice_pos, size_t n_delete, mstr_view insert) -{ - // Guard against self-insertion: - if (insert.data && _mstr_overlaps(str, insert)) { - // The insertion string exists within the current string. We cannot modify it in-place. - // Duplicate the insertion string to remain pristine while we splice: - mstr insert_dup = mstr_copy(insert); - if (!insert_dup.data) { - // Failed to dup the insert string. Failure to splice - return false; - } - // Do the splice, now using the copy of the insertion string - const bool ok = mstr_splice(str, splice_pos, n_delete, mstr_view_from(insert_dup)); - // We're done with the dup - mstr_destroy(&insert_dup); - // Return the sub-result - return ok; - } - mlib_check(splice_pos <= str->len); - // How many chars is it possible to delete from `splice_pos`? - size_t n_chars_avail_to_delete = str->len - splice_pos; - // Clamp to the number of chars available for deletion: - if (n_delete > n_chars_avail_to_delete) { - n_delete = n_chars_avail_to_delete; - } - // Compute the new string length - size_t new_len = str->len; - // This should never fail, because we should never try to delete more chars than we have - mlib_check(!mlib_sub(&new_len, n_delete)); - // Check if appending would make too big of a string - if (mlib_unlikely(mlib_add(&new_len, insert.len))) { - // New string will be too long - return false; - } - char *mut = str->data; - // We either resize first or resize last, depending on where we are shifting chars - if (new_len > str->len) { - // Do the resize first - if (!mstr_resize_for_overwrite(str, new_len)) { - // Failed to allocate - return false; - } - mut = str->data; - } - // Move to the splice position - mut += splice_pos; - // Shift the existing string parts around for the deletion operation - const size_t tail_len = n_chars_avail_to_delete - n_delete; - // Adjust to the begining of the string part that we want to keep - char *copy_from = mut + n_delete; - char *copy_to = mut + insert.len; - memmove(copy_to, copy_from, tail_len); - if (new_len < str->len) { - // We didn't resize first, so resize now. We are shrinking the string, so this - // will never fail, and does not create any uninitialized memory: - mlib_check(mstr_resize_for_overwrite(str, new_len)); - mut = str->data + splice_pos; - } - // Insert the new data if the insertion string is non-null - if (insert.data) { - memcpy(mut, insert.data, insert.len); - } - return true; -} - -/** - * @brief Append a string to the end of some other string. - * - * @param str The string to be modified - * @param suffix The suffix string to be appended onto `*str` - * @return true If the operation was successful - * @return false Otherwise - * - * If case of failure, `*str` is not modified. - */ -static inline bool -mstr_append(mstr *str, mstr_view suffix) -{ - return mstr_splice(str, str->len, 0, suffix); -} - -#define mstr_append(Into, Suffix) mstr_append((Into), mstr_view_from((Suffix))) - -/** - * @brief Append a single character to the given string object - * - * @param str The string object to be updated - * @param c The single character that will be inserted at the end - * @return true If the operation succeeded - * @return false Otherwise - * - * In case of failure, the string is not modified. - */ -static inline bool -mstr_append_char(mstr *str, char c) -{ - mstr_view one = mstr_view_data(&c, 1); - return mstr_append(str, one); -} - -/** - * @brief Replace every occurrence of `needle` in `str` with `sub` - * - * @param str The string object to be updated - * @param needle The non-empty needle string to be searched for.s - * @param sub The string to be inserted in place of each `needle` - * @return true If the operation succeeds - * @return false Otherwise - * - * @note If the `needle` string is empty, then the substitution string will - * be inserted around and between every byte in the string: - * - * replace("foo", "", "|") -> "|f|o|o|" - * - * @note The operation is guaranteed to never fail if the `sub` string is not - * longer than the `needle` string AND the needle and sub strings do not overlap - * - * @note If the operation fails, the content of `str` is an unspecified but valid - * string. - */ -static inline bool -mstr_replace(mstr *str, mstr_view needle, mstr_view sub) -{ - bool okay = true; - // We may dup the needle/sub if they overlap the output string - mstr needle_dup = mstr_null(); - mstr sub_dup = mstr_null(); - // Check if the needle is a substring of the target: - if (_mstr_overlaps(str, needle)) { - // Copy the needle string - needle_dup = mstr_copy(needle); - // Detect allocation failure: - okay = !!needle_dup.data; - // Update the needle to point to the duplicate: - needle = mstr_view_from(needle_dup); - } - // Do the same with the sub string: - if (okay && _mstr_overlaps(str, sub)) { - sub_dup = mstr_copy(sub); - okay = !!needle_dup.data; - sub = mstr_view_from(sub_dup); - } - // Scan forward, starting from the first position: - size_t off = 0; - while (okay && off <= str->len) { - // Find the next occurrence, starting from the scan offset - off = mstr_find(*str, needle, off); - if (off == SIZE_MAX) { - // No more occurrences. - break; - } - // Replace the needle string with the new value - if (!mstr_splice(str, off, needle.len, sub)) { - okay = false; - } - // Advance over the length of the replacement string, so we don't try to - // infinitely replace content if the replacement itself contains the needle - // string - if (mlib_unlikely(mlib_add(&off, sub.len))) { - // Integer overflow while advancing the offset. No good. - okay = false; - } - // Note: To support empty needles, advance one more space to avoid infinite - // repititions in-place. - // TODO: To do this "properly", this should instead advance over a full UTF-8-encoded - // codepoint. For now, just do a single byte. - if (!needle.len && mlib_unlikely(mlib_add(&off, 1))) { - // Advancing the extra distance failed - okay = false; - } - } - // Destroy the needle/sub strings, which we may have duplicated if they overlapped - // the target. If not, then these are a no-op. - mstr_destroy(&needle_dup); - mstr_destroy(&sub_dup); - return okay; -} - -/** - * @brief Like `mstr_sprintf`, but accepts a `va_list` directly. - */ -mlib_printf_attribute(1, 0) static inline mstr mstr_vsprintf(const char *format, va_list args) -{ - size_t format_strlen = strlen(format); - size_t sz = format_strlen; - if (mlib_unlikely(mlib_mul(&sz, 2))) { - // Overflow on multiply. Oof - sz = format_strlen; - } - - mstr ret = mstr_null(); - while (1) { - // Resize to make room for the formatted text - if (!mstr_resize(&ret, sz)) { - // Allocation failure - break; - } - - // Calc the size with the null terminator - size_t len_with_null = ret.len; - if (mlib_unlikely(mlib_add(&len_with_null, 1))) { - // Unlikely: Overflow - break; - } - - // Do the formatting - va_list dup_args; - va_copy(dup_args, args); - - // clang complains that the format string is not a string literal; - // this is fine since we're writing a vsnprintf wrapper - // the format string will be checked at call sites to mstr_vsprintf() - // because we mark it using mlib_printf_attribute -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wformat-nonliteral" -#endif - int n_chars = vsnprintf(ret.data, len_with_null, format, dup_args); -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic pop -#endif - va_end(dup_args); - - // On error, returns a negative value - if (n_chars < 0) { - break; - } - - if ((size_t)n_chars <= ret.len) { - // Success. Truncate to the number of chars actually written: - mstr_resize(&ret, (size_t)n_chars); - // Return the successfully formatted string: - return ret; - } - - // Need more room. Resize and try again: - sz = (size_t)n_chars; - continue; - } - - // Only reached if the operation failed - mstr_destroy(&ret); - return ret; -} - -/** - * @brief Format a string according to `printf` rules - * - * @param f The format string to be used. - * @param ... The formatting arguments to interpolate into the string - * @return mstr A new mstr upon success, or a null mstr upon failure. - */ -mlib_printf_attribute(1, 2) static inline mstr mstr_sprintf(const char *f, ...) -{ - va_list args; - va_start(args, f); - mstr ret = mstr_vsprintf(f, args); - va_end(args); - return ret; -} - -/** - * @brief Like `mstr_sprintf_append`, but accepts the va_list directly. - */ -mlib_printf_attribute(2, 0) static inline bool mstr_vsprintf_append(mstr *string, const char *format, va_list args) -{ - mlib_check(string != NULL, because, "Output string parameter is required"); - mstr suffix = mstr_vsprintf(format, args); - bool ok = mstr_append(string, suffix); - mstr_destroy(&suffix); - return ok; -} - -/** - * @brief Append content to a string using `printf()` style formatting. - * - * @param string Pointer to a valid or null string object which will be modified - * @param format A printf-style format string to append onto `string` - * @param ... The interpolation arguments for `format` - * - * @retval true If-and-only-if the string is successfully modified - * @retval false If there was an error during formatting. The content of `string` - * is unspecified. - * - * This function maintains the existing content of `string` and only inserts - * additional characters at the end of the string. - */ -mlib_printf_attribute(2, 3) static inline bool mstr_sprintf_append(mstr *string, const char *format, ...) -{ - va_list args; - va_start(args, format); - const bool okay = mstr_vsprintf_append(string, format, args); - va_end(args); - return okay; -} - - -#endif // MLIB_STR_H_INCLUDED diff --git a/bsonjs/common/mlib/str_vec.h b/bsonjs/common/mlib/str_vec.h deleted file mode 100644 index 8a69e63..0000000 --- a/bsonjs/common/mlib/str_vec.h +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @file str_vec.h - * @brief This file defines mstr_vec, a common "array of strings" type - * @date 2025-09-30 - * - * @copyright Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MLIB_STR_VEC_H_INCLUDED -#define MLIB_STR_VEC_H_INCLUDED - -#include -#include - -#define T mstr -#define VecDestroyElement(Ptr) (mstr_destroy(Ptr)) -#define VecCopyElement(Dst, Src) (*Dst = mstr_copy(*Src), Dst->data != NULL) -#include - -#endif // MLIB_STR_VEC_H_INCLUDED diff --git a/bsonjs/common/mlib/test.h b/bsonjs/common/mlib/test.h deleted file mode 100644 index c0f937f..0000000 --- a/bsonjs/common/mlib/test.h +++ /dev/null @@ -1,334 +0,0 @@ -/** - * @file mlib/test.h - * @brief Testing utilities - * @date 2025-01-30 - * - * @copyright Copyright (c) 2025 - * - * 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. - */ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -/** - * @brief Place this macro at the head of a (compound) statement to assert that - * executing that statement aborts the program with SIGABRT. - * - * Internally, this will fork the calling process and wait for the child process - * to terminate. It asserts that the child exits abnormally with SIGABRT. This - * test assertion is a no-op on Win32, since it does not have a suitable `fork` - * API. - * - * Beware that the child process runs in a forked environment, so it is not - * safe to use any non-fork-safe functionality, and any modifications to program - * state will not be visible in the parent. Behavior of attempting to escape the - * statement (goto/return) is undefined. - * - * If the child process does not abort, it will call `_Exit(71)` to indicate - * to the parent that it did not terminate (the number 71 is chosen arbitrarily) - * - * If the token `debug` is passed as a macro argument, then the forking behavior - * is suppressed, allowing for easier debugging of the statement. - */ -#define mlib_assert_aborts(...) MLIB_PASTE_3(_mlibAssertAbortsStmt, _, __VA_ARGS__)() - -#ifndef _WIN32 -#include -#include -#define _mlibAssertAbortsStmt_() \ - for (int once = 1, other_pid = fork(); once; once = 0) \ - for (; once; once = 0) \ - if (other_pid != 0) { \ - /* We are the parent */ \ - int wstatus; \ - waitpid(other_pid, &wstatus, 0); \ - if (WIFEXITED(wstatus)) { \ - /* Normal exit! */ \ - _mlib_stmt_did_not_abort(__FILE__, MLIB_FUNC, __LINE__, WEXITSTATUS(wstatus)); \ - } else if (WIFSIGNALED(wstatus)) { \ - /* Signalled */ \ - if (WTERMSIG(wstatus) != SIGABRT) { \ - fprintf(stderr, \ - "%s:%d: [%s]: Child process did not exit with SIGABRT! (Exited %d)\n", \ - __FILE__, \ - __LINE__, \ - MLIB_FUNC, \ - WTERMSIG(wstatus)); \ - fflush(stderr); \ - abort(); \ - } \ - } \ - } else /* We are the child */ \ - if ((fclose(stderr), 1)) \ - for (;; _Exit(71)) \ - for (;; _Exit(71)) /* Double loop to prevent the block from `break`ing out */ - -#else -#define _mlibAssertAbortsStmt_() \ - if (1) { \ - } else -#endif - -// Called when an assert-aborts statement does not terminate -static inline void -_mlib_stmt_did_not_abort(const char *file, const char *func, int line, int rc) -{ - /* Normal exit! */ - if (rc == 71) { - fprintf(stderr, "%s:%d: [%s]: Test case did not abort. The statement completed normally.\n", file, line, func); - } else { - fprintf(stderr, "%s:%d: [%s]: Test case did not abort (Exited %d)\n", file, line, func, rc); - } - fflush(stderr); - abort(); -} - -#define _mlibAssertAbortsStmt_debug() \ - for (;; _mlib_stmt_did_not_abort(__FILE__, MLIB_FUNC, __LINE__, -1)) \ - for (;; _mlib_stmt_did_not_abort(__FILE__, MLIB_FUNC, __LINE__, -1)) - -/** - * @brief Aggregate type that holds information about a source location - */ -typedef struct mlib_source_location { - const char *file; - int lineno; - const char *func; -} mlib_source_location; - -/** - * @brief Expands to an `mlib_source_location` for the location in which the macro is expanded - */ -#define mlib_this_source_location() (mlib_init(mlib_source_location){(__FILE__), (__LINE__), (MLIB_FUNC)}) -// ↑ The paren wrapping is required on VS2017 to prevent it from deleting the preceding comma (?!) - -/** - * @brief Evaluate a check, aborting with a diagnostic if that check fails - * - * Can be called with one argument to test a single boolean condition, or three - * arguments for more useful diagnostics with an infix operator. - */ -#define mlib_check(...) MLIB_ARGC_PICK(_mlib_check, #__VA_ARGS__, __VA_ARGS__) -// One arg: -#define _mlib_check_argc_2(ArgString, Condition) \ - _mlibCheckConditionSimple(Condition, ArgString, NULL, mlib_this_source_location()) -// Three args: -#define _mlib_check_argc_4(ArgString, A, Operator, B) \ - MLIB_NOTHING(#A, #B) MLIB_PASTE(_mlibCheckCondition_, Operator)(A, B, NULL) -// Five args: -#define _mlib_check_argc_6(ArgString, A, Operator, B, Infix, Reason) \ - MLIB_NOTHING(#A, #B) MLIB_PASTE(_mlib_check_with_suffix_, Infix)(A, Operator, B, Reason) -#define _mlib_check_with_suffix_because(A, Operator, B, Reason) \ - MLIB_NOTHING(#A, #B) MLIB_PASTE(_mlibCheckCondition_, Operator)(A, B, Reason) -// String-compare: -#define _mlibCheckCondition_str_eq(A, B, Reason) _mlibCheckStrEq(A, B, #A, #B, Reason, mlib_this_source_location()) -// Pointer-compare: -#define _mlibCheckCondition_ptr_eq(A, B, Reason) _mlibCheckPtrEq(A, B, #A, #B, Reason, mlib_this_source_location()) -// Integer-equal: -#define _mlibCheckCondition_eq(A, B, Reason) \ - _mlibCheckIntCmp(mlib_equal, \ - true, \ - "==", \ - mlib_upsize_integer(A), \ - mlib_upsize_integer(B), \ - #A, \ - #B, \ - Reason, \ - mlib_this_source_location()) -// Integer not-equal: -#define _mlibCheckCondition_neq(A, B, Reason) \ - _mlibCheckIntCmp(mlib_equal, \ - false, \ - "!=", \ - mlib_upsize_integer(A), \ - mlib_upsize_integer(B), \ - #A, \ - #B, \ - Reason, \ - mlib_this_source_location()) -// Integer comparisons: -#define _mlibCheckCondition_lt(A, B, Reason) \ - _mlibCheckIntCmp(mlib_less, \ - true, \ - "<", \ - mlib_upsize_integer(A), \ - mlib_upsize_integer(B), \ - #A, \ - #B, \ - Reason, \ - mlib_this_source_location()) -#define _mlibCheckCondition_lte(A, B, Reason) \ - _mlibCheckIntCmp(mlib_greater, \ - false, \ - "≤", \ - mlib_upsize_integer(A), \ - mlib_upsize_integer(B), \ - #A, \ - #B, \ - Reason, \ - mlib_this_source_location()) -#define _mlibCheckCondition_gt(A, B, Reason) \ - _mlibCheckIntCmp(mlib_greater, \ - true, \ - ">", \ - mlib_upsize_integer(A), \ - mlib_upsize_integer(B), \ - #A, \ - #B, \ - Reason, \ - mlib_this_source_location()) -#define _mlibCheckCondition_gte(A, B, Reason) \ - _mlibCheckIntCmp(mlib_less, \ - false, \ - "≥", \ - mlib_upsize_integer(A), \ - mlib_upsize_integer(B), \ - #A, \ - #B, \ - Reason, \ - mlib_this_source_location()) - - -// Simple assertion with an explanatory string -#define _mlibCheckCondition_because(Cond, Reason, _null) \ - _mlibCheckConditionSimple(Cond, #Cond, Reason, mlib_this_source_location()) - -/// Check evaluator when given a single boolean -static inline void -_mlibCheckConditionSimple(bool c, const char *expr, const char *reason, struct mlib_source_location here) -{ - if (!c) { - fprintf(stderr, "%s:%d: in [%s]: Check condition ⟨%s⟩ failed", here.file, here.lineno, here.func, expr); - if (reason) { - fprintf(stderr, " (%s)", reason); - } - fprintf(stderr, "\n"); - fflush(stderr); - abort(); - } -} - -// Implement integer comparison checks -static inline void -_mlibCheckIntCmp(enum mlib_cmp_result cres, // The cmp result to check - bool cond, // Whether we expect the cmp result to match `cres` - const char *operator_str, - struct mlib_upsized_integer left, - struct mlib_upsized_integer right, - const char *left_expr, - const char *right_expr, - const char *reason, - struct mlib_source_location here) -{ - if (((mlib_cmp)(left, right, 0) == cres) != cond) { - fprintf(stderr, - "%s:%d: in [%s]: Check [⟨%s⟩ %s ⟨%s⟩] failed:\n", - here.file, - here.lineno, - here.func, - left_expr, - operator_str, - right_expr); - fprintf(stderr, " "); - if (left.is_signed) { - fprintf(stderr, "%lld", (long long)left.bits.as_signed); - } else { - fprintf(stderr, "%llu", (unsigned long long)left.bits.as_unsigned); - } - fprintf(stderr, " ⟨%s⟩\n", left_expr); - fprintf(stderr, " "); - if (right.is_signed) { - fprintf(stderr, "%lld", (long long)right.bits.as_signed); - } else { - fprintf(stderr, "%llu", (unsigned long long)right.bits.as_unsigned); - } - fprintf(stderr, " ⟨%s⟩\n", right_expr); - if (reason) { - fprintf(stderr, "Because: %s\n", reason); - } - fflush(stderr); - abort(); - } -} - -// Pointer-comparison -static inline void -_mlibCheckPtrEq(const void *left, - const void *right, - const char *left_expr, - const char *right_expr, - const char *reason, - struct mlib_source_location here) -{ - if (left != right) { - fprintf(stderr, - "%s:%d: in [%s]: Check [⟨%s⟩ pointer-equal ⟨%s⟩] failed:\n", - here.file, - here.lineno, - here.func, - left_expr, - right_expr); - fprintf(stderr, - " %p ⟨%s⟩\n" - " ≠ %p ⟨%s⟩\n", - left, - left_expr, - right, - right_expr); - if (reason) { - fprintf(stderr, "Because: %s\n", reason); - } - fflush(stderr); - abort(); - } -} - -// String-comparison -static inline void -_mlibCheckStrEq(const char *left, - const char *right, - const char *left_expr, - const char *right_expr, - const char *reason, - struct mlib_source_location here) -{ - if (strcmp(left, right)) { - fprintf(stderr, - "%s:%d: in [%s]: Check [⟨%s⟩ str-equal ⟨%s⟩] failed:\n", - here.file, - here.lineno, - here.func, - left_expr, - right_expr); - fprintf(stderr, - " “%s” ⟨%s⟩\n" - " ≠ “%s” ⟨%s⟩\n", - left, - left_expr, - right, - right_expr); - if (reason) { - fprintf(stderr, "Because: %s\n", reason); - } - fflush(stderr); - abort(); - } -} diff --git a/bsonjs/common/mlib/time_point.h b/bsonjs/common/mlib/time_point.h deleted file mode 100644 index 7c8a5ac..0000000 --- a/bsonjs/common/mlib/time_point.h +++ /dev/null @@ -1,346 +0,0 @@ -/** - * @file mlib/time_point.h - * @brief A point-in-time type - * @date 2025-04-17 - * - * The `mlib_time_point` type represents a stable point-in-time. The time point - * itself is relative to a monotonic clock for the program, so it should not be - * transmitted or persisted outside of the execution of a program that uses it. - * - * @copyright Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MLIB_TIME_POINT_H_INCLUDED -#define MLIB_TIME_POINT_H_INCLUDED - -#include -#include -#include -#include - -// Check for POSIX clock functions functions -#undef mlib_have_posix_clocks -#define mlib_have_posix_clocks() 0 -#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || (defined(_DEFAULT_SOURCE) && !defined(_WIN32)) -#include -#undef mlib_have_posix_clocks -#define mlib_have_posix_clocks() 1 -#endif - -#include -#include -#include - -mlib_extern_c_begin(); - -/** - * @brief An abstract point-in-time type - * - * The time point is encoded as a duration relative to some stable reference point - * provided by the system. See the docs for the `time_since_monotonic_start` - * member for more details. - * - * At time of writing, there is no easy way to convert this monotonic time point - * into a human-readable wall time. Thus, the time-point itself is abstract. - */ -typedef struct mlib_time_point { - /** - * @brief The encoding of the time point as a duration relative to some - * unspecified stable real point in time. - * - * It is important to understand the nature of the reference point: `mlib_now()` - * uses the system's monotonic high-resolution clock, which has an unspecified - * reference point in the past. That stable reference point may change between - * program executions, so it is not safe to store/transmit this value outside - * of the current program execution. - * - * If you attempt to store a duration in this member that is with respect to - * some other clock, then the resulting time point object will have an unspecified - * relationship to other time points created with different clocks. For this reason, - * this member should not be set to any absolute values, and should only be adjusted - * relative to its current value. - */ - mlib_duration time_since_monotonic_start; -} mlib_time_point; - -/** - * @brief Given two time points, selects the time point that occurs earliest - */ -static inline mlib_time_point -mlib_earliest(mlib_time_point l, mlib_time_point r) -{ - l.time_since_monotonic_start = mlib_duration(l.time_since_monotonic_start, min, r.time_since_monotonic_start); - return l; -} - -/** - * @brief Given two time points, selects the time point that occurs later - */ -static inline mlib_time_point -mlib_latest(mlib_time_point l, mlib_time_point r) -{ - l.time_since_monotonic_start = mlib_duration(l.time_since_monotonic_start, max, r.time_since_monotonic_start); - return l; -} - -/** - * @brief Obtain the integer clock ID that is used by `mlib_now()` to obtain - * the time. This value only has meaning on POSIX systems. On Win32, returns - * INT_MIN. - * - * @return int The integer clock ID, corresponding to the value of a `CLOCK_...` - * object macro. - */ -static inline int -mlib_now_clockid(void) mlib_noexcept -{ -#ifdef CLOCK_MONOTONIC_RAW - // Linux had a bad definition of CLOCK_MONOTONIC, which would jump based on NTP adjustments. - // They replaced it with CLOCK_MONOTONIC_RAW, which is stable and cannot be adjusted. - return CLOCK_MONOTONIC_RAW; -#elif defined(CLOCK_MONOTONIC) - return CLOCK_MONOTONIC; -#else - return INT_MIN; -#endif -} - -/** - * @brief Obtain a point-in-time corresponding to the current time - */ -static inline mlib_time_point -mlib_now(void) mlib_noexcept -{ -#if mlib_have_posix_clocks() - // Use POSIX clock_gettime - struct timespec ts; - int rc = clock_gettime(mlib_now_clockid(), &ts); - // The above call must never fail: - mlib_check(rc, eq, 0); - // Encode the time point: - mlib_time_point ret; - ret.time_since_monotonic_start = mlib_duration_from_timespec(ts); - return ret; -#elif mlib_is_win32() - // Win32 APIs for the high-performance monotonic counter. These APIs never fail after Windows XP - LARGE_INTEGER freq; - QueryPerformanceFrequency(&freq); - LARGE_INTEGER lits; - QueryPerformanceCounter(&lits); - // Number of ticks of the perf counter - const int64_t ticks = lits.QuadPart; - // Number of ticks that the counter emits in one second - const int64_t ticks_per_second = freq.QuadPart; - // Do some math that avoids an integer overflow when converting to microseconds. - // Just one million, used to convert time units to microseconds. - const int64_t one_million = 1000000; - // Number of whole seconds that have elapsed: - const int64_t whole_seconds = ticks / ticks_per_second; - // Number of microseconds beyond the last whole second: - const int64_t subsecond_us = ((ticks % ticks_per_second) * one_million) / ticks_per_second; - mlib_time_point ret; - ret.time_since_monotonic_start = mlib_duration((whole_seconds, s), plus, (subsecond_us, us)); - return ret; -#else -#error We do not know how to get the current time on this platform -#endif -} - -/** - * @brief Obtain a point-in-time relative to a base time offset by the given - * duration (which may be negative). - * - * @param from The basis of the time offset - * @param delta The amount of time to shift the resulting time point - * @return mlib_time_point If 'delta' is a positive duration, the result is a - * point-in-time *after* 'from'. If 'delta' is a negative duration, the result - * is a point-in-time *before* 'from'. - */ -static inline mlib_time_point -mlib_time_add(mlib_time_point from, mlib_duration delta) mlib_noexcept -{ - mlib_time_point ret; - ret.time_since_monotonic_start = mlib_duration(from.time_since_monotonic_start, plus, delta); - return ret; -} - -#define mlib_time_add(From, Delta) mlib_time_add(From, mlib_duration_arg(Delta)) - -/** - * @brief Obtain the duration between two points in time. - * - * @param stop The target time - * @param start The base time - * @return mlib_duration The amount of time you would need to wait starting - * at 'start' for the time to become 'stop' (the result may be a negative - * duration). - * - * Intuition: If "stop" is "in the future" relative to "start", you will - * receive a positive duration, indicating an amount of time to wait - * beginning at 'start' to reach 'stop'. If "stop" is actually *before* - * "start", you will receive a paradoxical *negative* duration, indicating - * the amount of time needed to time-travel backwards to reach "stop." - */ -static inline mlib_duration -mlib_time_difference(mlib_time_point stop, mlib_time_point start) -{ - return mlib_duration(stop.time_since_monotonic_start, minus, start.time_since_monotonic_start); -} - -/** - * @brief Obtain the amount of time that has elapsed since the time point `t`, - * or a negative duration if the time is in the future. - * - * @param t The time point to be inspected - * @return mlib_duration If `t` is in the past, returns the duration of time - * that has elapsed since that point-in-time. If `t` is in the future, returns - * a negative time representing the amount of time that be waited until we - * reach `t`. - */ -static inline mlib_duration -mlib_elapsed_since(mlib_time_point t) -{ - return mlib_time_difference(mlib_now(), t); -} - -/** - * @brief Compare two time points to create an ordering. - * - * A time point "in the past" is "less than" a time point "in the future". - * - * @retval <0 If 'a' is before 'b' - * @retval >0 If 'b' is before 'a' - * @retval 0 If 'a' and 'b' are equivalent - * - * @note This is a function-like macro that can be called with an infix operator - * as the second argument to do natural time-point comparisons: - * - * ``` - * mlib_time_cmp(a, <=, b) - * ``` - */ -static inline enum mlib_cmp_result -mlib_time_cmp(mlib_time_point a, mlib_time_point b) mlib_noexcept -{ - return mlib_duration_cmp(a.time_since_monotonic_start, b.time_since_monotonic_start); -} - -#define mlib_time_cmp(...) MLIB_ARGC_PICK(_mlib_time_cmp, __VA_ARGS__) -#define _mlib_time_cmp_argc_2 mlib_time_cmp -#define _mlib_time_cmp_argc_3(L, Op, R) (mlib_time_cmp((L), (R)) Op 0) - -/** - * @brief Pause the calling thread until at least the specified duration has elapsed. - * - * @param d The duration of time to pause the thread. If this duration is zero - * or negative, then this function returns immediately. - * @return int An error code, if any occurred. Returns zero upon success, or - * the system's error number value (`errno` on POSIX, `GetLastError()` on - * Windows) - */ -static inline int -mlib_sleep_for(const mlib_duration d) mlib_noexcept -{ - mlib_duration_rep_t duration_usec = mlib_microseconds_count(d); - if (duration_usec <= 0) { - // Don't sleep any time - return 0; - } -#if mlib_have_posix_clocks() - // Convert the microseconds count to the value for the usleep function. We don't - // know the precise integer type that `usleep` expects, so do a checked-narrow - // to handle too-large values. - useconds_t i = 0; - if (mlib_narrow(&i, duration_usec)) { - // Too many microseconds. Sleep for the max. This will only be reached - // for positive durations because of the above check against `<= 0` - i = mlib_maxof(useconds_t); - } - int rc = usleep(i); - if (rc != 0) { - return errno; - } - return 0; -#elif defined(_WIN32) - DWORD retc = 0; - // Use WaitableTimer - const HANDLE timer = CreateWaitableTimerW(/* no attributes */ NULL, - /* Manual reset */ true, - /* Unnamed */ NULL); - // Check that we actually succeeded in creating a timer. - if (!timer) { - retc = GetLastError(); - goto done; - } - // Convert the number of microseconds into a count of 100ns intervals. Use - // a negative value to request a relative sleep time. - LONGLONG negative_n_100ns_units = 0; - if (mlib_mul(&negative_n_100ns_units, duration_usec, -10)) { - // Too many units. Clamp to the max duration (negative for a relative - // sleep): - negative_n_100ns_units = mlib_minof(LONGLONG); - } - LARGE_INTEGER due_time; - due_time.QuadPart = negative_n_100ns_units; - BOOL okay = SetWaitableTimer(/* The timer to modify */ timer, - /* The time after which it will fire */ &due_time, - /* Interval period 0 = only fire once */ 0, - /* No completion routine */ NULL, - /* No arg for no completion routine */ NULL, - /* Wake up the system if it goes to sleep */ true); - if (!okay) { - // Failed to set the timer. Hmm? - retc = GetLastError(); - goto done; - } - // Do the actual wait - DWORD rc = WaitForSingleObject(timer, INFINITE); - if (rc == WAIT_FAILED) { - // Executing the wait operation failed. - retc = GetLastError(); - goto done; - } - // Check for success: - mlib_check(rc, eq, WAIT_OBJECT_0); -done: - // Done with the timer. - if (timer) { - CloseHandle(timer); - } - return retc; -#else -#error "mlib_sleep_for" is not implemented on this platform. -#endif -} - -#define mlib_sleep_for(...) mlib_sleep_for(mlib_duration(__VA_ARGS__)) - -/** - * @brief Pause the calling thread until the given time point has been reached - * - * @param when The time point at which to resume, at soonest - * @return int A possible error code for the operation. Returns zero upon success. - * - * The `when` is the *soonest* successful wake time. The thread may wake at a later time. - */ -static inline int -mlib_sleep_until(const mlib_time_point when) mlib_noexcept -{ - const mlib_duration time_until = mlib_time_difference(when, mlib_now()); - return mlib_sleep_for(time_until); -} - -mlib_extern_c_end(); - -#endif // MLIB_TIME_POINT_H_INCLUDED diff --git a/bsonjs/common/mlib/timer.h b/bsonjs/common/mlib/timer.h deleted file mode 100644 index 41edd2d..0000000 --- a/bsonjs/common/mlib/timer.h +++ /dev/null @@ -1,173 +0,0 @@ -/** - * @file mlib/timer.h - * @brief Timer types and functions - * @date 2025-04-18 - * - * This file contains APIs for creating fixed-deadline timer objects that represent - * stable expiration points. - * - * @copyright Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MLIB_TIMER_H_INCLUDED -#define MLIB_TIMER_H_INCLUDED - -#include -#include -#include - -mlib_extern_c_begin(); - -/** - * @brief Represents an expiry timer. The timer stores some point-in-time - * after which it is considered to have "expired." - */ -typedef struct mlib_timer { - /** - * @brief The point-in-time at which the timer will be considered expired. - * - * This field can be updated or modified to change the expiration time of - * the timer. - */ - mlib_time_point expires_at; -} mlib_timer; - -/** - * @brief Create a deadline timer that expires at the given point-in-time - * - * @param t The point-in-time at which the returned timer should be expired - * @return mlib_timer - */ -static inline mlib_timer -mlib_expires_at(const mlib_time_point t) mlib_noexcept -{ - mlib_timer ret; - ret.expires_at = t; - return ret; -} - -/** - * @brief Create a deadline timer that expires after the given duration has - * elapsed from the point-in-time at which this function is called - */ -static inline mlib_timer -mlib_expires_after(const mlib_duration dur) mlib_noexcept -{ - const mlib_time_point later = mlib_time_add(mlib_now(), dur); - return mlib_expires_at(later); -} - -#define mlib_expires_after(...) mlib_expires_after(mlib_duration(__VA_ARGS__)) - -/** - * @brief Obtain a timer that will "never" expire - * - * In actuality, the timer expires at a time so far in the future that no computer - * program could ever hope to continue running to that point, and by the time - * that point is reached it will be some other civilization's problem. - */ -static inline mlib_timer -mlib_expires_never(void) mlib_noexcept -{ - mlib_timer t; - t.expires_at.time_since_monotonic_start = mlib_duration_max(); - return t; -} - -/** - * @brief Between two timers, return the timer that will expire the soonest - */ -static inline mlib_timer -mlib_soonest_timer(mlib_timer l, mlib_timer r) mlib_noexcept -{ - l.expires_at = mlib_earliest(l.expires_at, r.expires_at); - return l; -} - -/** - * @brief Obtain the duration of time that is remaining until the given timer - * expires. If the timer has expired, the returned duration will be zero (never - * negative) - */ -static inline mlib_duration -mlib_timer_remaining(const mlib_timer timer) mlib_noexcept -{ - // The duration until the expiry time of the timer - const mlib_duration remain = mlib_time_difference(timer.expires_at, mlib_now()); - if (mlib_duration_cmp(remain, <, mlib_duration())) { - // No time remaining. Return a zero duration (not a negative duration) - return mlib_duration(); - } - return remain; -} - -/** - * @brief Test for timer expiration - * - * @param timer The timer to be tested - * @param once (Optional) A pointer to an optional once-flag that will be set - * to `true` (see below) - * - * The function behaves as follows: - * - * - If `once` is a null pointer, then returns a boolean indicating whether the - * timer has expired. - * - Otherwise, if `*once` is `true`: - * - If `timer` has expired, returns `true` - * - Otherwise, `*once` is set to `true` and returns `false` - * - * The intent of the `once` flag is to support loops that check for expiry, - * where at least one iteration of the loop *must* be attempted, even if the - * timer has expired. For example: - * - * ``` - * void do_thing() { - * bool once = false; - * while (!mlib_timer_is_expired(timer, &once)) { - * try_thing(timer); - * } - * } - * ``` - * - * In the above, `try_thing` will be called *at least once*, even if the timer - * is already expired. - */ -static inline bool -mlib_timer_is_expired(const mlib_timer timer, bool *once) mlib_noexcept -{ - // Is the timer already expired? - const bool no_time_remaining = mlib_time_cmp(timer.expires_at, <=, mlib_now()); - if (!once) { - // Just return `true` if there is zero time remaining - return no_time_remaining; - } else { - // Tweak behavior based on the `*once` value - if (!*once) { - // This is the first time we have been called with the given once-flag - *once = true; - // Don't count an expiration, even if we have zero time left, because - // the caller wants to try some operation at least once - return false; - } - return no_time_remaining; - } -} - -mlib_extern_c_end(); - -#define mlib_timer_is_expired(...) MLIB_ARGC_PICK(_mlibTimerIsExpired, __VA_ARGS__) -#define _mlibTimerIsExpired_argc_1(Timer) mlib_timer_is_expired((Timer), NULL) -#define _mlibTimerIsExpired_argc_2(Timer, OncePtr) mlib_timer_is_expired((Timer), (OncePtr)) - -#endif // MLIB_TIMER_H_INCLUDED diff --git a/bsonjs/common/mlib/vec.th b/bsonjs/common/mlib/vec.th deleted file mode 100644 index 65f243a..0000000 --- a/bsonjs/common/mlib/vec.th +++ /dev/null @@ -1,470 +0,0 @@ -/** - * @file vec.th - * @brief Declare a new vector container data type - * @date 2024-10-02 - * - * To use this file: - * - * - #define a type `T` immediately before including this file. - * - Optional: Define an identifier `VecName` to the name of the vector. If unset, declares `_vec` - * - Optional: Define a `VecDestroyElement(Ptr)` macro to specify how the vector - * should destroy the element at `*Ptr`. If unset, destroying is a no-op. - * - Optional: Define `VecInitElement(Ptr, ...)` which initializes a new element. - * The first macro argument is a pointer to the element and subsequent arguments - * are unspecified and reserved for future use. Elements are zero-initialized - * before being passed to this macro. - * - Optional: Define `VecCopyElement(DstPtr, SrcPtr)` to copy data from `*SrcPtr` - * to `*DstPtr`. The vector's copying function is only defined if this macro - * is defined. This macro MUST evaluate to a boolean to indicate if the copy - * operation succeeded. If a copy fails, then the partially copied elements - * will be destroyed and the overall copy will fail. - * - * To add a trival copying function, define `VecCopyElement` to - * `VecTrivialCopyElement`. - * - * - NOTE: All of the above macros will be automatically undef'd after this file - * is included. - * - * Types stored in the vector must be trivially relocatable. - * - * @copyright Copyright 2009-present MongoDB, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include - -#include // assert -#include // bool -#include // size_t -#include // calloc, realloc, free -#include // memcpy, memset - -// Check that the caller provided a `T` macro to be the element type -#ifndef T -#if defined(__clangd__) || defined(__INTELLISENSE__) -#define T int // Define a type for IDE diagnostics -#define VecCopyElement VecTrivialCopyElement // For IDE highlighting -#else -#error A type `T` should be defined before including this file -#endif -#endif - -#ifndef VecName -#define VecName MLIB_PASTE(T, _vec) -#endif - -#ifndef VecDestroyElement -#define VecDestroyElement(Ptr) ((void)(Ptr)) -#endif - -#ifndef VecInitElement -#define VecInitElement(Ptr) ((void)(Ptr)) -#endif - -#ifndef VecTrivialCopyElement -#define VecTrivialCopyElement(DstPtr, SrcPtr) ((*(DstPtr) = *(SrcPtr)), true) -#endif - -#pragma push_macro("vec_inline_spec") -#if !defined(vec_inline_spec) -#define vec_inline_spec static inline -#endif - -// The "fn" macro just adds a qualified name to the front of a function identifier -#pragma push_macro("fn") -#undef fn -#define fn(M) MLIB_PASTE_3(VecName, _, M) - -typedef struct VecName { - /** - * @private - * @brief Pointer to the first vector element, or NULL if the vector is - * empty. - * - * @note DO NOT DIRECTLY MODIFY THIS VALUE - */ - T *data; - /** - * @brief The number of elements in the vector. - * - * @note DO NOT DIRECTLY MODIFY THIS VALUE - */ - size_t size; - /** - * @brief The number of allocated storage elements. - * - * @note DO NOT DIRECTLY MODIFY THIS VALUE - */ - size_t capacity; - -#if mlib_is_cxx() - T * - begin() noexcept - { - return data; - } - T * - end() noexcept - { - return data ? data + size : data; - } -#endif -} VecName; - -mlib_extern_c_begin(); - -/** - * @brief Obtain a pointer-to-mutable to the first element in the given vector - */ -vec_inline_spec T * -fn(begin(VecName *v)) mlib_noexcept -{ - return v->data; -} - -/** - * @brief Obtain a pointer-to-mutable past the last element in the given vector - */ -vec_inline_spec T * -fn(end(VecName *v)) mlib_noexcept -{ - return v->data ? v->data + v->size : v->data; -} - -/** - * @brief Obtain a pointer-to-const to the first element in the given vector - */ -vec_inline_spec T const * -fn(cbegin(VecName const *v)) mlib_noexcept -{ - return v->data; -} - -/** - * @brief Obtain a pointer-to-const past the last element in the given vector - */ -vec_inline_spec T const * -fn(cend(VecName const *v)) mlib_noexcept -{ - return v->data ? v->data + v->size : v->data; -} - -/** - * @brief Get the maximum number of elements that can be held in the vector of - * a certain type. - */ -vec_inline_spec size_t -fn(max_size(void)) mlib_noexcept -{ - // We compare against (signed) PTRDIFF_MAX because want to support the difference - // between two pointers. If we use the unsigned size, then we could have vectors - // with size that is too large to represent the difference between two sizes. - return PTRDIFF_MAX / sizeof(T); -} - -/** - * @brief Set the capacity of the given vector. - * - * @param self The vector object to be modified - * @param count The new capacity. If this is less than the current size, then - * the capacity will be capped at the size instead - * - * @retval true If-and-only-if the reallocation was successful - * @retval false If there was an error in allocating the buffer - */ -vec_inline_spec bool -fn(reserve(VecName *const self, size_t count)) mlib_noexcept -{ - // Check if this value is beyond the possible capacity of the vector - if (count > fn(max_size())) { - // Too many elements. We cannot allocate a region this large. - return false; - } - // Check if we are already at the requested capacity. - if (count == self->capacity) { - // No reallocation needed. - return true; - } - // Check if the caller is requesting a lower capacity than our current size - if (count < self->size) { - // We cannot shrink the capacity below the current size, so just shrink-to-fit - count = self->size; - } - // Impossible: We will never shrink below `self.size`, and if - // `self.size == 0` and `count == 0`, then we early-return'd above. - assert(count != 0); - // The number of bytes we need to allocate. Note that this cannot overflow - // because we guard against it by checking against `max_size()` - const size_t new_buffer_size = count * sizeof(T); - // Attempt to reallocate the region - T *const new_buffer = (T *)realloc(self->data, new_buffer_size); - if (!new_buffer) { - // Failed to reallocate a new storage region - return false; - } - // Successfully reallocated the buffer. Update our storage pointer. - self->data = new_buffer; - // Note the new capacity. - self->capacity = count; - return true; -} - -/** - * @brief Destroy elements in the vector at the specified range positions - * - * @param self The vector to be updated - * @param first Pointer to the first element to be destroyed - * @param last Pointer to the first element to NOT be destroyed - * - * Elements are destroyed and removed starting at the end. If `first == last`, - * this is a no-op. The given pointers must refer to vector elements, and `last` - * must be reachable by advancing `first` zero or more times. - */ -vec_inline_spec void -fn(erase(VecName *const self, T *const first, T *const last)) -{ - // Number of elements following the removed region - const size_t n_tail_elements = (size_t)(fn(end(self)) - last); - // Destroy elements in reverse order: - for (T *r_iter = last; r_iter != first; --r_iter) { - VecDestroyElement((r_iter - 1)); - --self->size; - } - // This mult cannot overflow because we can never contain enough elements to overflow PTRDIFF_MAX - const size_t n_bytes_to_shift = n_tail_elements * sizeof(T); - // If there are any elements to be shifted, shift them down over the removed region - if (n_bytes_to_shift) { - // Shift all tail elements down into their new position - memmove(first, last, n_bytes_to_shift); - } -} - -/** - * @brief Destroy a single element at the given zero-based index position - */ -vec_inline_spec void -fn(erase_at(VecName *const self, size_t pos)) -{ - fn(erase(self, fn(begin(self)) + pos, fn(end(self)))); -} - -/** - * @brief Resize the vector to hold the given number of elements - * - * Newly added elements are zero-initialized, or initailized using VecInitElement - * - * @retval true If-and-only-if the resize was successful - * @retval false If the function failed to allocate the new storage region - * - * @note Don't forget to check the return value for success! - */ -// mlib_nodiscard ("Check the returned bool to detect allocation failure") -vec_inline_spec bool -fn(resize(VecName *const self, size_t const count)) mlib_noexcept -{ - // Check if we aren't actually growing the vector. - if (count <= self->size) { - // We need to destroy elements at the tail. If `count == size`, this is a no-op. - if (self->data) { - fn(erase(self, fn(begin(self)) + count, fn(end(self)))); - } - return true; - } - - // We need to increase the capacity of the vector to hold the new elements - // Try to auto-grow capacity. Increase capacity by ×1.5 - const size_t half_current_capacity = self->capacity / 2; - size_t new_capacity = 0; - if (mlib_unlikely(mlib_add(&new_capacity, self->size, half_current_capacity))) { - // The auto growth amount would overflow, so just cap to the max size. - new_capacity = fn(max_size()); - } - // Check if our automatic growth is big enough to hold the requested number of elements - if (new_capacity < count) { - // The automatic growth factor is actually smaller than the number of new elements - // the caller wants, so we need to increase capacity to that level instead. - new_capacity = count; - } - // Try to reserve more storage - if (!fn(reserve(self, new_capacity))) { - // We failed to reserve the new storage region. The requested capacity may be too large, - // or we may have just run out of memory. - return false; - } - - // Pointer to where the new end will be - T *const new_end = fn(begin(self)) + count; - // Create a zero-initialized object to copy over the top of each new element. - T zero; - memset(&zero, 0, sizeof zero); - // Call init() on ever new element up until the new size - for (T *iter = fn(end(self)); iter != new_end; ++iter) { - *iter = zero; - (void)(VecInitElement((iter))); - } - - // Update the stored size - self->size = count; - return true; -} - -/** - * @brief Append another element, returning a pointer to that element. - * - * @return T* A pointer to the newly added element, or NULL in case of allocation failure. - */ -// mlib_nodiscard ("Check the returned pointer for failure") -vec_inline_spec T * -fn(push(VecName *self)) mlib_noexcept -{ - size_t count = self->size; - if (mlib_unlikely(mlib_add(&count, 1))) { - // Adding another element would overflow size_t. This is extremely unlikely, - // but precautionary. - return NULL; - } - if (!fn(resize(self, count))) { - // Failed to push another item - return NULL; - } - return fn(begin(self)) + count - 1; -} - -/** - * @brief Create a new empty vector - */ -vec_inline_spec VecName fn(new(void)) mlib_noexcept -{ - VecName ret = {NULL, 0, 0}; - return ret; -} - -/** - * @brief Destroy the pointed-to vector, freeing the associated data buffer. - * - * The pointed-to vector becomes valid storage for a new vector object. - */ -vec_inline_spec void -fn(destroy(VecName *self)) mlib_noexcept -{ - // Resizing to zero will destroy all elements - (void)fn(resize(self, 0)); - // Resizing won't necessarily free the data buffer. Do that now. - free(self->data); - self->capacity = 0; - self->data = NULL; -} - -/** - * @brief Create a new vector with `n` initialized elements - */ -vec_inline_spec VecName -fn(new_n(size_t n, bool *okay)) mlib_noexcept -{ - VecName ret = fn(new()); - *okay = fn(resize)(&ret, n); - return ret; -} - -#ifdef VecCopyElement -/** - * @brief Copy the data from the vector `src` into storage for a new vector `dst` - * - * @param dst_vec Pointer-to-storage for a new vector object to be initialized. - * @param src_vec Pointer to a vector whose elements will be copied into a new vector - * @retval true If-and-only-if the copy was successful. - * @retval false Otherwise - */ -vec_inline_spec bool -fn(init_copy(VecName *dst_vec, VecName const *src_vec)) mlib_noexcept -{ - VecName tmp = fn(new()); - // Try to reseve capacity for all new elements. Don't resize(), because we want - // uninitialized storage for the new data. - if (!fn(reserve(&tmp, src_vec->size))) { - // We failed to reserve capacity in the new vector - fn(destroy(&tmp)); - // Always leave `dst_vec` in an initialized state. - *dst_vec = (VecName){NULL, 0, 0}; - return false; - } - // Copy everything into the destination element-by-element - { - // Input iterator - T const *in_iter = fn(cbegin(src_vec)); - // Input stop position - T const *const in_stop = fn(cend(src_vec)); - // Output iterator - T *out_iter = tmp.data; - // Copy from the first to the last - for (; in_iter != in_stop; ++in_iter, ++out_iter) { - // Try to copy into the new element - if (!VecCopyElement((out_iter), (in_iter))) { - // Failed copying here. Undo everything by destroying the temporary - fn(destroy(&tmp)); - // Always leave `dst_vec` in an initialized state. - *dst_vec = (VecName){NULL, 0, 0}; - return false; - } - // Update the size of the temporary vec to record that it is holding the new - // element. This allows us to call `destroy()` to undo our work. - tmp.size++; - } - } - // Everything went okay. Give the temporary to the caller as the final result - *dst_vec = tmp; - return true; -} -#endif // VecCopyElement - -#ifndef mlib_vec_foreach -#define mlib_vec_foreach(Type, VarName, Vector) \ - for (Type *VarName = (Vector).data; VarName && (VarName != (Vector).data + (Vector).size); ++VarName) -#endif - -#ifndef mlib_vec_at -/** - * @brief Obtain a vector element at some zero-based index offset, with negative index - * wrapping (-1 refers to the last element in the vector) - * - * @note The `Vec` argument will be evaluated at least twice! - */ -#define mlib_vec_at(Vec, Pos) ((Vec).data[_mlib_vec_index_adjust((Vec).size, mlib_upsize_integer(Pos))]) -static inline size_t -_mlib_vec_index_adjust(size_t size, mlib_upsized_integer pos) -{ - if (pos.is_signed && pos.bits.as_signed < 0) { - return mlib_assert_add(size_t, size, pos.bits.as_signed); - } - mlib_check(pos.bits.as_unsigned, lte, size, because, "the vector index must be in-bounds for mlib_vec_at()"); - return pos.bits.as_unsigned; -} -#endif - -mlib_extern_c_end(); - -#undef T -#undef VecName -#undef VecDestroyElement -#undef VecInitElement -#undef VecTrivialCopyElement -#ifdef VecCopyElement -#undef VecCopyElement -#endif -// These ones we want to pop, not undefine: -#pragma pop_macro("fn") -#pragma pop_macro("vec_inline_spec") - -// vi: ft=c diff --git a/bsonjs/jsonsl/LICENSE b/bsonjs/jsonsl/LICENSE deleted file mode 100644 index e021f06..0000000 --- a/bsonjs/jsonsl/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2012-2015 M. Nunberg, mnunberg@haskalah.org - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/bsonjs/jsonsl/jsonsl.c b/bsonjs/jsonsl/jsonsl.c deleted file mode 100644 index 92e6b73..0000000 --- a/bsonjs/jsonsl/jsonsl.c +++ /dev/null @@ -1,1577 +0,0 @@ -/* Copyright (C) 2012-2015 Mark Nunberg. - * - * See included LICENSE file for license details. - */ - -#include -#include - -#include -#include - -#define CASE_DIGITS \ -case '1': \ -case '2': \ -case '3': \ -case '4': \ -case '5': \ -case '6': \ -case '7': \ -case '8': \ -case '9': \ -case '0': - -static unsigned extract_special(unsigned); -static int is_special_end(unsigned); -static int is_allowed_whitespace(unsigned); -static int is_allowed_escape(unsigned); -static int is_simple_char(unsigned); -static char get_escape_equiv(unsigned); - -jsonsl_t jsonsl_new(int nlevels) -{ - unsigned int ii; - struct jsonsl_st * jsn; - - if (nlevels < 2) { - return NULL; - } - - jsn = (struct jsonsl_st *) - bson_malloc0(sizeof (*jsn) + - ( (nlevels) * sizeof (struct jsonsl_state_st) ) - ); - - jsn->levels_max = (unsigned int) nlevels; - jsn->max_callback_level = UINT_MAX; - jsonsl_reset(jsn); - for (ii = 0; ii < jsn->levels_max; ii++) { - jsn->stack[ii].level = ii; - } - return jsn; -} - -void jsonsl_reset(jsonsl_t jsn) -{ - jsn->tok_last = 0; - jsn->can_insert = 1; - jsn->pos = 0; - jsn->level = 0; - jsn->stopfl = 0; - jsn->in_escape = 0; - jsn->expecting = 0; -} - -void jsonsl_destroy(jsonsl_t jsn) -{ - if (jsn) { - bson_free(jsn); - } -} - - -#define FASTPARSE_EXHAUSTED 1 -#define FASTPARSE_BREAK 0 - -/* - * This function is meant to accelerate string parsing, reducing the main loop's - * check if we are indeed a string. - * - * @param jsn the parser - * @param[in,out] bytes_p A pointer to the current buffer (i.e. current position) - * @param[in,out] nbytes_p A pointer to the current size of the buffer - * @return true if all bytes have been exhausted (and thus the main loop can - * return), false if a special character was examined which requires greater - * examination. - */ -static int -jsonsl__str_fastparse(jsonsl_t jsn, - const jsonsl_uchar_t **bytes_p, size_t *nbytes_p) -{ - const jsonsl_uchar_t *bytes = *bytes_p; - const jsonsl_uchar_t *end; - for (end = bytes + *nbytes_p; bytes != end; bytes++) { - if ( -#ifdef JSONSL_USE_WCHAR - *bytes >= 0x100 || -#endif /* JSONSL_USE_WCHAR */ - (is_simple_char(*bytes))) { - } else { - /* Once we're done here, re-calculate the position variables */ - jsn->pos += (bytes - *bytes_p); - *nbytes_p -= (bytes - *bytes_p); - *bytes_p = bytes; - return FASTPARSE_BREAK; - } - } - - /* Once we're done here, re-calculate the position variables */ - jsn->pos += (bytes - *bytes_p); - return FASTPARSE_EXHAUSTED; -} - -/* Functions exactly like str_fastparse, except it also accepts a 'state' - * argument, since the number's value is updated in the state. */ -static int -jsonsl__num_fastparse(jsonsl_t jsn, - const jsonsl_uchar_t **bytes_p, size_t *nbytes_p, - struct jsonsl_state_st *state) -{ - int exhausted = 1; - size_t nbytes = *nbytes_p; - const jsonsl_uchar_t *bytes = *bytes_p; - - for (; nbytes; nbytes--, bytes++) { - jsonsl_uchar_t c = *bytes; - if (isdigit(c)) { - state->nelem = (state->nelem * 10) + (c - 0x30); - } else { - exhausted = 0; - break; - } - } - jsn->pos += (*nbytes_p - nbytes); - if (exhausted) { - return FASTPARSE_EXHAUSTED; - } - *nbytes_p = nbytes; - *bytes_p = bytes; - return FASTPARSE_BREAK; -} - -void -jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes) -{ - -#define INVOKE_ERROR(eb) \ - if (1) { \ - if (jsn->error_callback(jsn, JSONSL_ERROR_##eb, state, (char*)c)) { \ - goto GT_AGAIN; \ - } \ - return; \ - } else ((void)0) - -#define STACK_PUSH \ - if (1) { \ - if (jsn->level >= (levels_max-1)) { \ - jsn->error_callback(jsn, JSONSL_ERROR_LEVELS_EXCEEDED, state, (char*)c); \ - return; \ - } \ - state = jsn->stack + (++jsn->level); \ - state->ignore_callback = jsn->stack[jsn->level-1].ignore_callback; \ - state->pos_begin = jsn->pos; \ - } else ((void)0) - -#define CALLBACK_AND_POP_NOPOS(T) \ - if (1) { \ - state->pos_cur = jsn->pos; \ - DO_CALLBACK(T, POP); \ - state->nescapes = 0; \ - state = jsn->stack + (--jsn->level); \ - } else ((void)0) - -#define CALLBACK_AND_POP(T) \ - if (1) { \ - CALLBACK_AND_POP_NOPOS(T); \ - state->pos_cur = jsn->pos; \ - } else ((void)0) - -#define SPECIAL_POP \ - if (1) { \ - CALLBACK_AND_POP(SPECIAL); \ - jsn->expecting = 0; \ - jsn->tok_last = 0; \ - } else ((void)0) - -#define CUR_CHAR (*(jsonsl_uchar_t*)c) - -#define DO_CALLBACK(T, action) \ - if (jsn->call_##T && \ - jsn->max_callback_level > state->level && \ - state->ignore_callback == 0) { \ - \ - if (jsn->action_callback_##action) { \ - jsn->action_callback_##action(jsn, JSONSL_ACTION_##action, state, (jsonsl_char_t*)c); \ - } else if (jsn->action_callback) { \ - jsn->action_callback(jsn, JSONSL_ACTION_##action, state, (jsonsl_char_t*)c); \ - } \ - if (jsn->stopfl) { return; } \ - } else ((void)0) - - /** - * Verifies that we are able to insert the (non-string) item into a hash. - */ -#define ENSURE_HVAL \ - if (state->nelem % 2 == 0 && state->type == JSONSL_T_OBJECT) { \ - INVOKE_ERROR(HKEY_EXPECTED); \ - } else ((void)0) - -#define VERIFY_SPECIAL(lit, lit_len) \ - if ((jsn->pos - state->pos_begin) > lit_len \ - || CUR_CHAR != (lit)[jsn->pos - state->pos_begin]) { \ - INVOKE_ERROR(SPECIAL_EXPECTED); \ - } else ((void)0) - -#define VERIFY_SPECIAL_CI(lit, lit_len) \ - if ((jsn->pos - state->pos_begin) > lit_len \ - || tolower(CUR_CHAR) != (lit)[jsn->pos - state->pos_begin]) { \ - INVOKE_ERROR(SPECIAL_EXPECTED); \ - } else ((void)0) - -#define STATE_SPECIAL_LENGTH \ - (state)->nescapes - -#define IS_NORMAL_NUMBER \ - ((state)->special_flags == JSONSL_SPECIALf_UNSIGNED || \ - (state)->special_flags == JSONSL_SPECIALf_SIGNED) - -#define STATE_NUM_LAST jsn->tok_last - -#define CONTINUE_NEXT_CHAR() continue - - const jsonsl_uchar_t *c = (jsonsl_uchar_t*)bytes; - size_t levels_max = jsn->levels_max; - struct jsonsl_state_st *state = jsn->stack + jsn->level; - jsn->base = bytes; - - for (; nbytes; nbytes--, jsn->pos++, c++) { - unsigned state_type; - - GT_AGAIN: - state_type = state->type; - /* Most common type is typically a string: */ - if (state_type & JSONSL_Tf_STRINGY) { - /* Special escape handling for some stuff */ - if (jsn->in_escape) { - jsn->in_escape = 0; - if (!is_allowed_escape(CUR_CHAR)) { - INVOKE_ERROR(ESCAPE_INVALID); - } else if (CUR_CHAR == 'u') { - DO_CALLBACK(UESCAPE, UESCAPE); - if (jsn->return_UESCAPE) { - return; - } - } - CONTINUE_NEXT_CHAR(); - } - - if (jsonsl__str_fastparse(jsn, &c, &nbytes) == - FASTPARSE_EXHAUSTED) { - /* No need to readjust variables as we've exhausted the iterator */ - return; - } else { - if (CUR_CHAR == '"') { - goto GT_QUOTE; - } else if (CUR_CHAR == '\\') { - goto GT_ESCAPE; - } else { - INVOKE_ERROR(WEIRD_WHITESPACE); - } - } - - } else if (state_type == JSONSL_T_SPECIAL) { - /* Fast track for signed/unsigned */ - if (IS_NORMAL_NUMBER) { - if (jsonsl__num_fastparse(jsn, &c, &nbytes, state) == - FASTPARSE_EXHAUSTED) { - return; - } else { - goto GT_SPECIAL_NUMERIC; - } - } else if (state->special_flags == JSONSL_SPECIALf_DASH) { -#ifdef JSONSL_PARSE_NAN - if (CUR_CHAR == 'I' || CUR_CHAR == 'i') { - /* parsing -Infinity? */ - state->special_flags = JSONSL_SPECIALf_NEG_INF; - CONTINUE_NEXT_CHAR(); - } -#endif - - if (!isdigit(CUR_CHAR)) { - INVOKE_ERROR(INVALID_NUMBER); - } - - if (CUR_CHAR == '0') { - state->special_flags = JSONSL_SPECIALf_ZERO|JSONSL_SPECIALf_SIGNED; - } else if (isdigit(CUR_CHAR)) { - state->special_flags = JSONSL_SPECIALf_SIGNED; - state->nelem = CUR_CHAR - 0x30; - } else { - INVOKE_ERROR(INVALID_NUMBER); - } - CONTINUE_NEXT_CHAR(); - - } else if (state->special_flags == JSONSL_SPECIALf_ZERO) { - if (isdigit(CUR_CHAR)) { - /* Following a zero! */ - INVOKE_ERROR(INVALID_NUMBER); - } - /* Unset the 'zero' flag: */ - if (state->special_flags & JSONSL_SPECIALf_SIGNED) { - state->special_flags = JSONSL_SPECIALf_SIGNED; - } else { - state->special_flags = JSONSL_SPECIALf_UNSIGNED; - } - goto GT_SPECIAL_NUMERIC; - } - - if ((state->special_flags & JSONSL_SPECIALf_NUMERIC) && - !(state->special_flags & JSONSL_SPECIALf_INF)) { - GT_SPECIAL_NUMERIC: - switch (CUR_CHAR) { - CASE_DIGITS - STATE_NUM_LAST = '1'; - CONTINUE_NEXT_CHAR(); - - case '.': - if (state->special_flags & JSONSL_SPECIALf_FLOAT) { - INVOKE_ERROR(INVALID_NUMBER); - } - state->special_flags |= JSONSL_SPECIALf_FLOAT; - STATE_NUM_LAST = '.'; - CONTINUE_NEXT_CHAR(); - - case 'e': - case 'E': - if (state->special_flags & JSONSL_SPECIALf_EXPONENT) { - INVOKE_ERROR(INVALID_NUMBER); - } - state->special_flags |= JSONSL_SPECIALf_EXPONENT; - STATE_NUM_LAST = 'e'; - CONTINUE_NEXT_CHAR(); - - case '-': - case '+': - if (STATE_NUM_LAST != 'e') { - INVOKE_ERROR(INVALID_NUMBER); - } - STATE_NUM_LAST = '-'; - CONTINUE_NEXT_CHAR(); - - default: - if (is_special_end(CUR_CHAR)) { - goto GT_SPECIAL_POP; - } - INVOKE_ERROR(INVALID_NUMBER); - break; - } - } - /* else if (!NUMERIC) */ - if (!is_special_end(CUR_CHAR)) { - STATE_SPECIAL_LENGTH++; - - /* Verify TRUE, FALSE, NULL */ - if (state->special_flags == JSONSL_SPECIALf_TRUE) { - VERIFY_SPECIAL("true", 4 /* strlen("true") */); - } else if (state->special_flags == JSONSL_SPECIALf_FALSE) { - VERIFY_SPECIAL("false", 5 /* strlen("false") */); - } else if (state->special_flags == JSONSL_SPECIALf_NULL) { - VERIFY_SPECIAL("null", 4 /* strlen("null") */); -#ifdef JSONSL_PARSE_NAN - } else if (state->special_flags == JSONSL_SPECIALf_POS_INF) { - VERIFY_SPECIAL_CI("infinity", 8 /* strlen("infinity") */); - } else if (state->special_flags == JSONSL_SPECIALf_NEG_INF) { - VERIFY_SPECIAL_CI("-infinity", 9 /* strlen("-infinity") */); - } else if (state->special_flags == JSONSL_SPECIALf_NAN) { - VERIFY_SPECIAL_CI("nan", 3 /* strlen("nan") */); - } else if (state->special_flags & JSONSL_SPECIALf_NULL || - state->special_flags & JSONSL_SPECIALf_NAN) { - /* previous char was "n", are we parsing null or nan? */ - 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 (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 - } - CONTINUE_NEXT_CHAR(); - } - - GT_SPECIAL_POP: - jsn->can_insert = 0; - if (IS_NORMAL_NUMBER) { - /* Nothing */ - } else if (state->special_flags == JSONSL_SPECIALf_ZERO || - state->special_flags == (JSONSL_SPECIALf_ZERO|JSONSL_SPECIALf_SIGNED)) { - /* 0 is unsigned! */ - state->special_flags = JSONSL_SPECIALf_UNSIGNED; - } else if (state->special_flags == JSONSL_SPECIALf_DASH) { - /* Still in dash! */ - INVOKE_ERROR(INVALID_NUMBER); - } else if (state->special_flags & JSONSL_SPECIALf_INF) { - if (STATE_SPECIAL_LENGTH != 8) { - INVOKE_ERROR(SPECIAL_INCOMPLETE); - } - state->nelem = 1; - } else if (state->special_flags & JSONSL_SPECIALf_NUMERIC) { - /* Check that we're not at the end of a token */ - if (STATE_NUM_LAST != '1') { - INVOKE_ERROR(INVALID_NUMBER); - } - } else if (state->special_flags == JSONSL_SPECIALf_TRUE) { - if (STATE_SPECIAL_LENGTH != 4) { - INVOKE_ERROR(SPECIAL_INCOMPLETE); - } - state->nelem = 1; - } else if (state->special_flags == JSONSL_SPECIALf_FALSE) { - if (STATE_SPECIAL_LENGTH != 5) { - INVOKE_ERROR(SPECIAL_INCOMPLETE); - } - } else if (state->special_flags == JSONSL_SPECIALf_NULL) { - if (STATE_SPECIAL_LENGTH != 4) { - INVOKE_ERROR(SPECIAL_INCOMPLETE); - } - } - SPECIAL_POP; - jsn->expecting = ','; - if (is_allowed_whitespace(CUR_CHAR)) { - CONTINUE_NEXT_CHAR(); - } - /** - * This works because we have a non-whitespace token - * which is not a special token. If this is a structural - * character then it will be gracefully handled by the - * switch statement. Otherwise it will default to the 'special' - * state again, - */ - goto GT_STRUCTURAL_TOKEN; - } else if (is_allowed_whitespace(CUR_CHAR)) { - /* So we're not special. Harmless insignificant whitespace - * passthrough - */ - CONTINUE_NEXT_CHAR(); - } else if (extract_special(CUR_CHAR)) { - /* not a string, whitespace, or structural token. must be special */ - goto GT_SPECIAL_BEGIN; - } - - if (CUR_CHAR == '"') { - GT_QUOTE: - jsn->can_insert = 0; - switch (state_type) { - - /* the end of a string or hash key */ - case JSONSL_T_STRING: - CALLBACK_AND_POP(STRING); - CONTINUE_NEXT_CHAR(); - case JSONSL_T_HKEY: - CALLBACK_AND_POP(HKEY); - CONTINUE_NEXT_CHAR(); - - case JSONSL_T_OBJECT: - state->nelem++; - if ( (state->nelem-1) % 2 ) { - /* Odd, this must be a hash value */ - if (jsn->tok_last != ':') { - INVOKE_ERROR(MISSING_TOKEN); - } - jsn->expecting = ','; /* Can't figure out what to expect next */ - jsn->tok_last = 0; - - STACK_PUSH; - state->type = JSONSL_T_STRING; - DO_CALLBACK(STRING, PUSH); - - } else { - /* hash key */ - if (jsn->expecting != '"') { - INVOKE_ERROR(STRAY_TOKEN); - } - jsn->tok_last = 0; - jsn->expecting = ':'; - - STACK_PUSH; - state->type = JSONSL_T_HKEY; - DO_CALLBACK(HKEY, PUSH); - } - CONTINUE_NEXT_CHAR(); - - case JSONSL_T_LIST: - state->nelem++; - STACK_PUSH; - state->type = JSONSL_T_STRING; - jsn->expecting = ','; - jsn->tok_last = 0; - DO_CALLBACK(STRING, PUSH); - CONTINUE_NEXT_CHAR(); - - case JSONSL_T_SPECIAL: - INVOKE_ERROR(STRAY_TOKEN); - break; - - default: - INVOKE_ERROR(STRING_OUTSIDE_CONTAINER); - break; - } /* switch(state->type) */ - } else if (CUR_CHAR == '\\') { - GT_ESCAPE: - /* Escape */ - if ( (state->type & JSONSL_Tf_STRINGY) == 0 ) { - INVOKE_ERROR(ESCAPE_OUTSIDE_STRING); - } - state->nescapes++; - jsn->in_escape = 1; - CONTINUE_NEXT_CHAR(); - } /* " or \ */ - - GT_STRUCTURAL_TOKEN: - switch (CUR_CHAR) { - case ':': - if (jsn->expecting != CUR_CHAR) { - INVOKE_ERROR(STRAY_TOKEN); - } - jsn->tok_last = ':'; - jsn->can_insert = 1; - jsn->expecting = '"'; - CONTINUE_NEXT_CHAR(); - - case ',': - /** - * The comma is one of the more generic tokens. - * In the context of an OBJECT, the can_insert flag - * should never be set, and no other action is - * necessary. - */ - if (jsn->expecting != CUR_CHAR) { - /* make this branch execute only when we haven't manually - * just placed the ',' in the expecting register. - */ - INVOKE_ERROR(STRAY_TOKEN); - } - - if (state->type == JSONSL_T_OBJECT) { - /* end of hash value, expect a string as a hash key */ - jsn->expecting = '"'; - } else { - jsn->can_insert = 1; - } - - jsn->tok_last = ','; - jsn->expecting = '"'; - CONTINUE_NEXT_CHAR(); - - /* new list or object */ - /* hashes are more common */ - case '{': - case '[': - if (!jsn->can_insert) { - INVOKE_ERROR(CANT_INSERT); - } - - ENSURE_HVAL; - state->nelem++; - - STACK_PUSH; - /* because the constants match the opening delimiters, we can do this: */ - state->type = CUR_CHAR; - state->nelem = 0; - jsn->can_insert = 1; - if (CUR_CHAR == '{') { - /* If we're a hash, we expect a key first, which is quouted */ - jsn->expecting = '"'; - } - if (CUR_CHAR == JSONSL_T_OBJECT) { - DO_CALLBACK(OBJECT, PUSH); - } else { - DO_CALLBACK(LIST, PUSH); - } - jsn->tok_last = 0; - CONTINUE_NEXT_CHAR(); - - /* closing of list or object */ - case '}': - case ']': - if (jsn->tok_last == ',' && jsn->options.allow_trailing_comma == 0) { - INVOKE_ERROR(TRAILING_COMMA); - } - - jsn->can_insert = 0; - jsn->level--; - jsn->expecting = ','; - jsn->tok_last = 0; - if (CUR_CHAR == ']') { - if (state->type != '[') { - INVOKE_ERROR(BRACKET_MISMATCH); - } - DO_CALLBACK(LIST, POP); - } else { - if (state->type != '{') { - INVOKE_ERROR(BRACKET_MISMATCH); - } else if (state->nelem && state->nelem % 2 != 0) { - INVOKE_ERROR(VALUE_EXPECTED); - } - DO_CALLBACK(OBJECT, POP); - } - state = jsn->stack + jsn->level; - state->pos_cur = jsn->pos; - CONTINUE_NEXT_CHAR(); - - default: - GT_SPECIAL_BEGIN: - /** - * Not a string, not a structural token, and not benign whitespace. - * Technically we should iterate over the character always, but since - * we are not doing full numerical/value decoding anyway (but only hinting), - * we only check upon entry. - */ - if (state->type != JSONSL_T_SPECIAL) { - int special_flags = extract_special(CUR_CHAR); - if (!special_flags) { - /** - * Try to do some heuristics here anyway to figure out what kind of - * error this is. The 'special' case is a fallback scenario anyway. - */ - if (CUR_CHAR == '\0') { - INVOKE_ERROR(FOUND_NULL_BYTE); - } else if (CUR_CHAR < 0x20) { - INVOKE_ERROR(WEIRD_WHITESPACE); - } else { - INVOKE_ERROR(SPECIAL_EXPECTED); - } - } - ENSURE_HVAL; - state->nelem++; - if (!jsn->can_insert) { - INVOKE_ERROR(CANT_INSERT); - } - STACK_PUSH; - state->type = JSONSL_T_SPECIAL; - state->special_flags = special_flags; - STATE_SPECIAL_LENGTH = 1; - - if (special_flags == JSONSL_SPECIALf_UNSIGNED) { - state->nelem = CUR_CHAR - 0x30; - STATE_NUM_LAST = '1'; - } else { - STATE_NUM_LAST = '-'; - state->nelem = 0; - } - DO_CALLBACK(SPECIAL, PUSH); - } - CONTINUE_NEXT_CHAR(); - } - } -} - -const char* jsonsl_strerror(jsonsl_error_t err) -{ - if (err == JSONSL_ERROR_SUCCESS) { - return "SUCCESS"; - } -#define X(t) \ - if (err == JSONSL_ERROR_##t) \ - return #t; - JSONSL_XERR -#undef X - return ""; -} - -const char *jsonsl_strtype(jsonsl_type_t type) -{ -#define X(o,c) \ - if (type == JSONSL_T_##o) \ - return #o; - JSONSL_XTYPE -#undef X - return "UNKNOWN TYPE"; - -} - -/* - * - * JPR/JSONPointer functions - * - * - */ -static -jsonsl_jpr_type_t -populate_component(char *in, - struct jsonsl_jpr_component_st *component, - char **next, - jsonsl_error_t *errp) -{ - unsigned long pctval; - char *c = NULL, *outp = NULL, *end = NULL; - size_t input_len; - jsonsl_jpr_type_t ret = JSONSL_PATH_NONE; - - if (*next == NULL || *(*next) == '\0') { - return JSONSL_PATH_NONE; - } - - /* Replace the next / with a NULL */ - *next = strstr(in, "/"); - if (*next != NULL) { - *(*next) = '\0'; /* drop the forward slash */ - input_len = *next - in; - end = *next; - *next += 1; /* next character after the '/' */ - } else { - input_len = strlen(in); - end = in + input_len + 1; - } - - component->pstr = in; - - /* Check for special components of interest */ - if (*in == JSONSL_PATH_WILDCARD_CHAR && input_len == 1) { - /* Lone wildcard */ - ret = JSONSL_PATH_WILDCARD; - goto GT_RET; - } else if (isdigit(*in)) { - /* ASCII Numeric */ - char *endptr; - component->idx = strtoul(in, &endptr, 10); - if (endptr && *endptr == '\0') { - ret = JSONSL_PATH_NUMERIC; - goto GT_RET; - } - } - - /* Default, it's a string */ - ret = JSONSL_PATH_STRING; - for (c = outp = in; c < end; c++, outp++) { - char origc; - if (*c != '%') { - goto GT_ASSIGN; - } - /* - * c = { [+0] = '%', [+1] = 'b', [+2] = 'e', [+3] = '\0' } - */ - - /* Need %XX */ - if (c+2 >= end) { - *errp = JSONSL_ERROR_PERCENT_BADHEX; - return JSONSL_PATH_INVALID; - } - if (! (isxdigit(*(c+1)) && isxdigit(*(c+2))) ) { - *errp = JSONSL_ERROR_PERCENT_BADHEX; - return JSONSL_PATH_INVALID; - } - - /* Temporarily null-terminate the characters */ - origc = *(c+3); - *(c+3) = '\0'; - pctval = strtoul(c+1, NULL, 16); - *(c+3) = origc; - - *outp = (char) pctval; - c += 2; - continue; - - GT_ASSIGN: - *outp = *c; - } - /* Null-terminate the string */ - for (; outp < c; outp++) { - *outp = '\0'; - } - - GT_RET: - component->ptype = ret; - if (ret != JSONSL_PATH_WILDCARD) { - component->len = strlen(component->pstr); - } - return ret; -} - -jsonsl_jpr_t -jsonsl_jpr_new(const char *path, jsonsl_error_t *errp) -{ - char *my_copy = NULL; - int count, curidx; - struct jsonsl_jpr_st *ret = NULL; - struct jsonsl_jpr_component_st *components = NULL; - size_t origlen; - jsonsl_error_t errstacked; - -#define JPR_BAIL(err) if (1) { *errp = err; goto GT_ERROR; } else ((void)0) - - if (errp == NULL) { - errp = &errstacked; - } - - if (path == NULL || *path != '/') { - JPR_BAIL(JSONSL_ERROR_JPR_NOROOT); - } - - count = 1; - path++; - { - const char *c = path; - for (; *c; c++) { - if (*c == '/') { - count++; - if (*(c+1) == '/') { - JPR_BAIL(JSONSL_ERROR_JPR_DUPSLASH); - } - } - } - } - if(*path) { - count++; - } - - components = (struct jsonsl_jpr_component_st *) - malloc(sizeof(*components) * count); - if (!components) { - JPR_BAIL(JSONSL_ERROR_ENOMEM); - } - - my_copy = (char *)malloc(strlen(path) + 1); - if (!my_copy) { - JPR_BAIL(JSONSL_ERROR_ENOMEM); - } - - strcpy(my_copy, path); - - components[0].ptype = JSONSL_PATH_ROOT; - - if (*my_copy) { - char *cur = my_copy; - int pathret = JSONSL_PATH_STRING; - curidx = 1; - while (curidx < count) { - pathret = populate_component(cur, components + curidx, &cur, errp); - if (pathret > 0) { - curidx++; - } else { - break; - } - } - - if (pathret == JSONSL_PATH_INVALID) { - JPR_BAIL(JSONSL_ERROR_JPR_BADPATH); - } - } else { - curidx = 1; - } - - path--; /*revert path to leading '/' */ - origlen = strlen(path) + 1; - ret = (struct jsonsl_jpr_st *)malloc(sizeof(*ret)); - if (!ret) { - JPR_BAIL(JSONSL_ERROR_ENOMEM); - } - ret->orig = (char *)malloc(origlen); - if (!ret->orig) { - JPR_BAIL(JSONSL_ERROR_ENOMEM); - } - ret->components = components; - ret->ncomponents = curidx; - ret->basestr = my_copy; - ret->norig = origlen-1; - strcpy(ret->orig, path); - - return ret; - - GT_ERROR: - free(my_copy); - free(components); - if (ret) { - free(ret->orig); - } - free(ret); - return NULL; -#undef JPR_BAIL -} - -void jsonsl_jpr_destroy(jsonsl_jpr_t jpr) -{ - free(jpr->components); - free(jpr->basestr); - free(jpr->orig); - free(jpr); -} - -/** - * Call when there is a possibility of a match, either as a final match or - * as a path within a match - * @param jpr The JPR path - * @param component Component corresponding to the current element - * @param prlevel The level of the *parent* - * @param chtype The type of the child - * @return Match status - */ -static jsonsl_jpr_match_t -jsonsl__match_continue(jsonsl_jpr_t jpr, - const struct jsonsl_jpr_component_st *component, - unsigned prlevel, unsigned chtype) -{ - const struct jsonsl_jpr_component_st *next_comp = component + 1; - if (prlevel == jpr->ncomponents - 1) { - /* This is the match. Check the expected type of the match against - * the child */ - if (jpr->match_type == 0 || jpr->match_type == chtype) { - return JSONSL_MATCH_COMPLETE; - } else { - return JSONSL_MATCH_TYPE_MISMATCH; - } - } - if (chtype == JSONSL_T_LIST) { - if (next_comp->ptype == JSONSL_PATH_NUMERIC) { - return JSONSL_MATCH_POSSIBLE; - } else { - return JSONSL_MATCH_TYPE_MISMATCH; - } - } else if (chtype == JSONSL_T_OBJECT) { - if (next_comp->ptype == JSONSL_PATH_NUMERIC) { - return JSONSL_MATCH_TYPE_MISMATCH; - } else { - return JSONSL_MATCH_POSSIBLE; - } - } else { - return JSONSL_MATCH_TYPE_MISMATCH; - } -} - -jsonsl_jpr_match_t -jsonsl_path_match(jsonsl_jpr_t jpr, - const struct jsonsl_state_st *parent, - const struct jsonsl_state_st *child, - const char *key, size_t nkey) -{ - const struct jsonsl_jpr_component_st *comp; - if (!parent) { - /* No parent. Return immediately since it's always a match */ - return jsonsl__match_continue(jpr, jpr->components, 0, child->type); - } - - comp = jpr->components + parent->level; - - /* note that we don't need to verify the type of the match, this is - * always done through the previous call to jsonsl__match_continue. - * If we are in a POSSIBLE tree then we can be certain the types (at - * least at this level) are correct */ - if (parent->type == JSONSL_T_OBJECT) { - if (comp->len != nkey || strncmp(key, comp->pstr, nkey) != 0) { - return JSONSL_MATCH_NOMATCH; - } - } else { - if (comp->idx != parent->nelem - 1) { - return JSONSL_MATCH_NOMATCH; - } - } - return jsonsl__match_continue(jpr, comp, parent->level, child->type); -} - -jsonsl_jpr_match_t -jsonsl_jpr_match(jsonsl_jpr_t jpr, - unsigned int parent_type, - unsigned int parent_level, - const char *key, - size_t nkey) -{ - /* find our current component. This is the child level */ - int cmpret; - struct jsonsl_jpr_component_st *p_component; - p_component = jpr->components + parent_level; - - if (parent_level >= jpr->ncomponents) { - return JSONSL_MATCH_NOMATCH; - } - - /* Lone query for 'root' element. Always matches */ - if (parent_level == 0) { - if (jpr->ncomponents == 1) { - return JSONSL_MATCH_COMPLETE; - } else { - return JSONSL_MATCH_POSSIBLE; - } - } - - /* Wildcard, always matches */ - if (p_component->ptype == JSONSL_PATH_WILDCARD) { - if (parent_level == jpr->ncomponents-1) { - return JSONSL_MATCH_COMPLETE; - } else { - return JSONSL_MATCH_POSSIBLE; - } - } - - /* Check numeric array index. This gets its special block so we can avoid - * string comparisons */ - if (p_component->ptype == JSONSL_PATH_NUMERIC) { - if (parent_type == JSONSL_T_LIST) { - if (p_component->idx != nkey) { - /* Wrong index */ - return JSONSL_MATCH_NOMATCH; - } else { - if (parent_level == jpr->ncomponents-1) { - /* This is the last element of the path */ - return JSONSL_MATCH_COMPLETE; - } else { - /* Intermediate element */ - return JSONSL_MATCH_POSSIBLE; - } - } - } else if (p_component->is_arridx) { - /* Numeric and an array index (set explicitly by user). But not - * a list for a parent */ - return JSONSL_MATCH_TYPE_MISMATCH; - } - } else if (parent_type == JSONSL_T_LIST) { - return JSONSL_MATCH_TYPE_MISMATCH; - } - - /* Check lengths */ - if (p_component->len != nkey) { - return JSONSL_MATCH_NOMATCH; - } - - /* Check string comparison */ - cmpret = strncmp(p_component->pstr, key, nkey); - if (cmpret == 0) { - if (parent_level == jpr->ncomponents-1) { - return JSONSL_MATCH_COMPLETE; - } else { - return JSONSL_MATCH_POSSIBLE; - } - } - - return JSONSL_MATCH_NOMATCH; -} - -void jsonsl_jpr_match_state_init(jsonsl_t jsn, - jsonsl_jpr_t *jprs, - size_t njprs) -{ - size_t ii, *firstjmp; - if (njprs == 0) { - return; - } - jsn->jprs = BSON_ARRAY_ALLOC(njprs, jsonsl_jpr_t); - jsn->jpr_count = njprs; - jsn->jpr_root = BSON_ARRAY_ALLOC0(njprs * jsn->levels_max, size_t); - memcpy(jsn->jprs, jprs, sizeof(jsonsl_jpr_t) * njprs); - /* Set the initial jump table values */ - - firstjmp = jsn->jpr_root; - for (ii = 0; ii < njprs; ii++) { - firstjmp[ii] = ii+1; - } -} - -void jsonsl_jpr_match_state_cleanup(jsonsl_t jsn) -{ - if (jsn->jpr_count == 0) { - return; - } - - bson_free(jsn->jpr_root); - bson_free(jsn->jprs); - jsn->jprs = NULL; - jsn->jpr_root = NULL; - jsn->jpr_count = 0; -} - -/** - * This function should be called exactly once on each element... - * This should also be called in recursive order, since we rely - * on the parent having been initialized for a match. - * - * Since the parent is checked for a match as well, we maintain a 'serial' counter. - * Whenever we traverse an element, we expect the serial to be the same as a global - * integer. If they do not match, we re-initialize the context, and set the serial. - * - * This ensures a type of consistency without having a proactive reset by the - * main lexer itself. - * - */ -jsonsl_jpr_t jsonsl_jpr_match_state(jsonsl_t jsn, - struct jsonsl_state_st *state, - const char *key, - size_t nkey, - jsonsl_jpr_match_t *out) -{ - struct jsonsl_state_st *parent_state; - jsonsl_jpr_t ret = NULL; - - /* Jump and JPR tables for our own state and the parent state */ - size_t *jmptable, *pjmptable; - size_t jmp_cur, ii, ourjmpidx; - - if (!jsn->jpr_root) { - *out = JSONSL_MATCH_NOMATCH; - return NULL; - } - - pjmptable = jsn->jpr_root + (jsn->jpr_count * (state->level-1)); - jmptable = pjmptable + jsn->jpr_count; - - /* If the parent cannot match, then invalidate it */ - if (*pjmptable == 0) { - *jmptable = 0; - *out = JSONSL_MATCH_NOMATCH; - return NULL; - } - - parent_state = jsn->stack + state->level - 1; - - if (parent_state->type == JSONSL_T_LIST) { - nkey = (size_t) parent_state->nelem; - } - - *jmptable = 0; - ourjmpidx = 0; - memset(jmptable, 0, sizeof(int) * jsn->jpr_count); - - for (ii = 0; ii < jsn->jpr_count; ii++) { - jmp_cur = pjmptable[ii]; - if (jmp_cur) { - jsonsl_jpr_t jpr = jsn->jprs[jmp_cur-1]; - *out = jsonsl_jpr_match(jpr, - parent_state->type, - parent_state->level, - key, nkey); - if (*out == JSONSL_MATCH_COMPLETE) { - ret = jpr; - *jmptable = 0; - return ret; - } else if (*out == JSONSL_MATCH_POSSIBLE) { - jmptable[ourjmpidx] = ii+1; - ourjmpidx++; - } - } else { - break; - } - } - if (!*jmptable) { - *out = JSONSL_MATCH_NOMATCH; - } - return NULL; -} - -const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match) -{ -#define X(T,v) \ - if ( match == JSONSL_MATCH_##T ) \ - return #T; - JSONSL_XMATCH -#undef X - return ""; -} - -static char * -jsonsl__writeutf8(uint32_t pt, char *out) -{ - #define ADD_OUTPUT(c) if (1) { *out = (char)(c); out++; } else ((void)0) - - if (pt < 0x80) { - ADD_OUTPUT(pt); - } else if (pt < 0x800) { - ADD_OUTPUT((pt >> 6) | 0xC0); - ADD_OUTPUT((pt & 0x3F) | 0x80); - } else if (pt < 0x10000) { - ADD_OUTPUT((pt >> 12) | 0xE0); - ADD_OUTPUT(((pt >> 6) & 0x3F) | 0x80); - ADD_OUTPUT((pt & 0x3F) | 0x80); - } else { - ADD_OUTPUT((pt >> 18) | 0xF0); - ADD_OUTPUT(((pt >> 12) & 0x3F) | 0x80); - ADD_OUTPUT(((pt >> 6) & 0x3F) | 0x80); - ADD_OUTPUT((pt & 0x3F) | 0x80); - } - return out; - #undef ADD_OUTPUT -} - -/* Thanks snej (https://github.com/mnunberg/jsonsl/issues/9) */ -static int -jsonsl__digit2int(char ch) { - int d = ch - '0'; - if ((unsigned) d < 10) { - return d; - } - d = ch - 'a'; - if ((unsigned) d < 6) { - return d + 10; - } - d = ch - 'A'; - if ((unsigned) d < 6) { - return d + 10; - } - return -1; -} - -/* Assume 's' is at least 4 bytes long */ -static int -jsonsl__get_uescape_16(const char *s) -{ - int ret = 0; - int cur; - - #define GET_DIGIT(off) \ - if (1) { \ - cur = jsonsl__digit2int(s[off]); \ - if (cur == -1) { return -1; } \ - ret |= (cur << (12 - (off * 4))); \ - } else ((void)0) - - GET_DIGIT(0); - GET_DIGIT(1); - GET_DIGIT(2); - GET_DIGIT(3); - #undef GET_DIGIT - return ret; -} - -/** - * Utility function to convert escape sequences - */ -size_t jsonsl_util_unescape_ex(const char *in, - char *out, - size_t len, - const int toEscape[128], - unsigned *oflags, - jsonsl_error_t *err, - const char **errat) -{ - const unsigned char *c = (const unsigned char*)in; - char *begin_p = out; - unsigned oflags_s; - uint16_t last_codepoint = 0; - - if (!oflags) { - oflags = &oflags_s; - } - *oflags = 0; - - #define UNESCAPE_BAIL(e,offset) \ - if (1) { \ - *err = JSONSL_ERROR_##e; \ - if (errat) { \ - *errat = (const char*)(c+ (ptrdiff_t)(offset)); \ - } \ - return 0; \ - } else ((void)0) - - for (; len; len--, c++, out++) { - int uescval; - if (*c != '\\') { - /* Not an escape, so we don't care about this */ - goto GT_ASSIGN; - } - - if (len < 2) { - UNESCAPE_BAIL(ESCAPE_INVALID, 0); - } - if (!is_allowed_escape(c[1])) { - UNESCAPE_BAIL(ESCAPE_INVALID, 1); - } - if ((toEscape && toEscape[(unsigned char)c[1] & 0x7f] == 0 && - c[1] != '\\' && c[1] != '"')) { - /* if we don't want to unescape this string, write the escape sequence to the output */ - *out++ = *c++; - --len; - goto GT_ASSIGN; - } - - if (c[1] != 'u') { - /* simple skip-and-replace using pre-defined maps. - * TODO: should the maps actually reflect the desired - * replacement character in toEscape? - */ - char esctmp = get_escape_equiv(c[1]); - if (esctmp) { - /* Check if there is a corresponding replacement */ - *out = esctmp; - } else { - /* Just gobble up the 'reverse-solidus' */ - *out = c[1]; - } - len--; - c++; - /* do not assign, just continue */ - continue; - } - - /* next == 'u' */ - if (len < 6) { - /* Need at least six characters.. */ - UNESCAPE_BAIL(UESCAPE_TOOSHORT, 2); - } - - uescval = jsonsl__get_uescape_16((const char *)c + 2); - if (uescval == -1) { - UNESCAPE_BAIL(PERCENT_BADHEX, -1); - } - - if (last_codepoint) { - uint16_t w1 = last_codepoint, w2 = (uint16_t)uescval; - uint32_t cp; - - if (uescval < 0xDC00 || uescval > 0xDFFF) { - UNESCAPE_BAIL(INVALID_CODEPOINT, -1); - } - - cp = (w1 & 0x3FF) << 10; - cp |= (w2 & 0x3FF); - cp += 0x10000; - - out = jsonsl__writeutf8(cp, out) - 1; - last_codepoint = 0; - - } else if (uescval < 0xD800 || uescval > 0xDFFF) { - *oflags |= JSONSL_SPECIALf_NONASCII; - out = jsonsl__writeutf8(uescval, out) - 1; - - } else if (uescval < 0xDC00) { - *oflags |= JSONSL_SPECIALf_NONASCII; - last_codepoint = (uint16_t)uescval; - out--; - } else { - UNESCAPE_BAIL(INVALID_CODEPOINT, 2); - } - - /* Post uescape cleanup */ - len -= 5; /* Gobble up 5 chars after 'u' */ - c += 5; - continue; - - /* Only reached by previous branches */ - GT_ASSIGN: - *out = *c; - } - - if (last_codepoint) { - *err = JSONSL_ERROR_INVALID_CODEPOINT; - return 0; - } - - *err = JSONSL_ERROR_SUCCESS; - return out - begin_p; -} - -/** - * Character Table definitions. - * These were all generated via srcutil/genchartables.pl - */ - -/** - * This table contains the beginnings of non-string - * allowable (bareword) values. - */ -static unsigned short Special_Table[0x100] = { - /* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ - /* 0x20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x2c */ - /* 0x2d */ JSONSL_SPECIALf_DASH /* <-> */, /* 0x2d */ - /* 0x2e */ 0,0, /* 0x2f */ - /* 0x30 */ JSONSL_SPECIALf_ZERO /* <0> */, /* 0x30 */ - /* 0x31 */ JSONSL_SPECIALf_UNSIGNED /* <1> */, /* 0x31 */ - /* 0x32 */ JSONSL_SPECIALf_UNSIGNED /* <2> */, /* 0x32 */ - /* 0x33 */ JSONSL_SPECIALf_UNSIGNED /* <3> */, /* 0x33 */ - /* 0x34 */ JSONSL_SPECIALf_UNSIGNED /* <4> */, /* 0x34 */ - /* 0x35 */ JSONSL_SPECIALf_UNSIGNED /* <5> */, /* 0x35 */ - /* 0x36 */ JSONSL_SPECIALf_UNSIGNED /* <6> */, /* 0x36 */ - /* 0x37 */ JSONSL_SPECIALf_UNSIGNED /* <7> */, /* 0x37 */ - /* 0x38 */ JSONSL_SPECIALf_UNSIGNED /* <8> */, /* 0x38 */ - /* 0x39 */ JSONSL_SPECIALf_UNSIGNED /* <9> */, /* 0x39 */ - /* 0x3a */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x48 */ - /* 0x49 */ JSONSL__INF_PROXY /* */, /* 0x49 */ - /* 0x4a */ 0,0,0,0, /* 0x4d */ - /* 0x4e */ JSONSL__NAN_PROXY /* */, /* 0x4e */ - /* 0x4f */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x65 */ - /* 0x66 */ JSONSL_SPECIALf_FALSE /* */, /* 0x66 */ - /* 0x67 */ 0,0, /* 0x68 */ - /* 0x69 */ JSONSL__INF_PROXY /* */, /* 0x69 */ - /* 0x6a */ 0,0,0,0, /* 0x6d */ - /* 0x6e */ JSONSL_SPECIALf_NULL|JSONSL__NAN_PROXY /* */, /* 0x6e */ - /* 0x6f */ 0,0,0,0,0, /* 0x73 */ - /* 0x74 */ JSONSL_SPECIALf_TRUE /* */, /* 0x74 */ - /* 0x75 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x94 */ - /* 0x95 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb4 */ - /* 0xb5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xd4 */ - /* 0xd5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xf4 */ - /* 0xf5 */ 0,0,0,0,0,0,0,0,0,0, /* 0xfe */ -}; - -/** - * Contains characters which signal the termination of any of the 'special' bareword - * values. - */ -static int Special_Endings[0x100] = { - /* 0x00 */ 0,0,0,0,0,0,0,0,0, /* 0x08 */ - /* 0x09 */ 1 /* */, /* 0x09 */ - /* 0x0a */ 1 /* */, /* 0x0a */ - /* 0x0b */ 0,0, /* 0x0c */ - /* 0x0d */ 1 /* */, /* 0x0d */ - /* 0x0e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ - /* 0x20 */ 1 /* */, /* 0x20 */ - /* 0x21 */ 0, /* 0x21 */ - /* 0x22 */ 1 /* " */, /* 0x22 */ - /* 0x23 */ 0,0,0,0,0,0,0,0,0, /* 0x2b */ - /* 0x2c */ 1 /* , */, /* 0x2c */ - /* 0x2d */ 0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x39 */ - /* 0x3a */ 1 /* : */, /* 0x3a */ - /* 0x3b */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5a */ - /* 0x5b */ 1 /* [ */, /* 0x5b */ - /* 0x5c */ 1 /* \ */, /* 0x5c */ - /* 0x5d */ 1 /* ] */, /* 0x5d */ - /* 0x5e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x7a */ - /* 0x7b */ 1 /* { */, /* 0x7b */ - /* 0x7c */ 0, /* 0x7c */ - /* 0x7d */ 1 /* } */, /* 0x7d */ - /* 0x7e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x9d */ - /* 0x9e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xbd */ - /* 0xbe */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xdd */ - /* 0xde */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xfd */ - /* 0xfe */ 0 /* 0xfe */ -}; - -/** - * This table contains entries for the allowed whitespace as per RFC 4627 - */ -static int Allowed_Whitespace[0x100] = { - /* 0x00 */ 0,0,0,0,0,0,0,0,0, /* 0x08 */ - /* 0x09 */ 1 /* */, /* 0x09 */ - /* 0x0a */ 1 /* */, /* 0x0a */ - /* 0x0b */ 0,0, /* 0x0c */ - /* 0x0d */ 1 /* */, /* 0x0d */ - /* 0x0e */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ - /* 0x20 */ 1 /* */, /* 0x20 */ - /* 0x21 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x40 */ - /* 0x41 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x60 */ - /* 0x61 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x80 */ - /* 0x81 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xa0 */ - /* 0xa1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xc0 */ - /* 0xc1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xe0 */ - /* 0xe1 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* 0xfe */ -}; - -static const int String_No_Passthrough[0x100] = { - /* 0x00 */ 1 /* */, /* 0x00 */ - /* 0x01 */ 1 /* */, /* 0x01 */ - /* 0x02 */ 1 /* */, /* 0x02 */ - /* 0x03 */ 1 /* */, /* 0x03 */ - /* 0x04 */ 1 /* */, /* 0x04 */ - /* 0x05 */ 1 /* */, /* 0x05 */ - /* 0x06 */ 1 /* */, /* 0x06 */ - /* 0x07 */ 1 /* */, /* 0x07 */ - /* 0x08 */ 1 /* */, /* 0x08 */ - /* 0x09 */ 1 /* */, /* 0x09 */ - /* 0x0a */ 1 /* */, /* 0x0a */ - /* 0x0b */ 1 /* */, /* 0x0b */ - /* 0x0c */ 1 /* */, /* 0x0c */ - /* 0x0d */ 1 /* */, /* 0x0d */ - /* 0x0e */ 1 /* */, /* 0x0e */ - /* 0x0f */ 1 /* */, /* 0x0f */ - /* 0x10 */ 1 /* */, /* 0x10 */ - /* 0x11 */ 1 /* */, /* 0x11 */ - /* 0x12 */ 1 /* */, /* 0x12 */ - /* 0x13 */ 1 /* */, /* 0x13 */ - /* 0x14 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x21 */ - /* 0x22 */ 1 /* <"> */, /* 0x22 */ - /* 0x23 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x42 */ - /* 0x43 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5b */ - /* 0x5c */ 1 /* <\> */, /* 0x5c */ - /* 0x5d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x7c */ - /* 0x7d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x9c */ - /* 0x9d */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xbc */ - /* 0xbd */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xdc */ - /* 0xdd */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xfc */ - /* 0xfd */ 0,0, /* 0xfe */ -}; - -/** - * Allowable two-character 'common' escapes: - */ -static int Allowed_Escapes[0x100] = { - /* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ - /* 0x20 */ 0,0, /* 0x21 */ - /* 0x22 */ 1 /* <"> */, /* 0x22 */ - /* 0x23 */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x2e */ - /* 0x2f */ 1 /* */, /* 0x2f */ - /* 0x30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x4f */ - /* 0x50 */ 0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5b */ - /* 0x5c */ 1 /* <\> */, /* 0x5c */ - /* 0x5d */ 0,0,0,0,0, /* 0x61 */ - /* 0x62 */ 1 /* */, /* 0x62 */ - /* 0x63 */ 0,0,0, /* 0x65 */ - /* 0x66 */ 1 /* */, /* 0x66 */ - /* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */ - /* 0x6e */ 1 /* */, /* 0x6e */ - /* 0x6f */ 0,0,0, /* 0x71 */ - /* 0x72 */ 1 /* */, /* 0x72 */ - /* 0x73 */ 0, /* 0x73 */ - /* 0x74 */ 1 /* */, /* 0x74 */ - /* 0x75 */ 1 /* */, /* 0x75 */ - /* 0x76 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x95 */ - /* 0x96 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb5 */ - /* 0xb6 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xd5 */ - /* 0xd6 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xf5 */ - /* 0xf6 */ 0,0,0,0,0,0,0,0,0, /* 0xfe */ -}; - -/** - * This table contains the _values_ for a given (single) escaped character. - */ -static unsigned char Escape_Equivs[0x100] = { - /* 0x00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x1f */ - /* 0x20 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x3f */ - /* 0x40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x5f */ - /* 0x60 */ 0,0, /* 0x61 */ - /* 0x62 */ 8 /* */, /* 0x62 */ - /* 0x63 */ 0,0,0, /* 0x65 */ - /* 0x66 */ 12 /* */, /* 0x66 */ - /* 0x67 */ 0,0,0,0,0,0,0, /* 0x6d */ - /* 0x6e */ 10 /* */, /* 0x6e */ - /* 0x6f */ 0,0,0, /* 0x71 */ - /* 0x72 */ 13 /* */, /* 0x72 */ - /* 0x73 */ 0, /* 0x73 */ - /* 0x74 */ 9 /* */, /* 0x74 */ - /* 0x75 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x94 */ - /* 0x95 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xb4 */ - /* 0xb5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xd4 */ - /* 0xd5 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0xf4 */ - /* 0xf5 */ 0,0,0,0,0,0,0,0,0,0 /* 0xfe */ -}; - -/* Definitions of above-declared static functions */ -static char get_escape_equiv(unsigned c) { - return Escape_Equivs[c & 0xff]; -} -static unsigned extract_special(unsigned c) { - return Special_Table[c & 0xff]; -} -static int is_special_end(unsigned c) { - return Special_Endings[c & 0xff]; -} -static int is_allowed_whitespace(unsigned c) { - return c == ' ' || Allowed_Whitespace[c & 0xff]; -} -static int is_allowed_escape(unsigned c) { - return Allowed_Escapes[c & 0xff]; -} -static int is_simple_char(unsigned c) { - return !String_No_Passthrough[c & 0xff]; -} - -/* Clean up all our macros! */ -#undef CASE_DIGITS -#undef INVOKE_ERROR -#undef STACK_PUSH -#undef CALLBACK_AND_POP_NOPOS -#undef CALLBACK_AND_POP -#undef SPECIAL_POP -#undef CUR_CHAR -#undef DO_CALLBACK -#undef ENSURE_HVAL -#undef VERIFY_SPECIAL -#undef STATE_SPECIAL_LENGTH -#undef IS_NORMAL_NUMBER -#undef STATE_NUM_LAST -#undef FASTPARSE_EXHAUSTED -#undef FASTPARSE_BREAK diff --git a/bsonjs/jsonsl/jsonsl.h b/bsonjs/jsonsl/jsonsl.h deleted file mode 100644 index e605867..0000000 --- a/bsonjs/jsonsl/jsonsl.h +++ /dev/null @@ -1,976 +0,0 @@ -/** - * JSON Simple/Stacked/Stateful Lexer. - * - Does not buffer data - * - Maintains state - * - Callback oriented - * - Lightweight and fast. One source file and one header file - * - * Copyright (C) 2012-2015 Mark Nunberg - * See included LICENSE file for license details. - */ - -#ifndef JSONSL_H_ -#define JSONSL_H_ - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifdef JSONSL_USE_WCHAR -typedef jsonsl_char_t wchar_t; -typedef jsonsl_uchar_t unsigned wchar_t; -#else -typedef char jsonsl_char_t; -typedef unsigned char jsonsl_uchar_t; -#endif /* JSONSL_USE_WCHAR */ - -#ifdef JSONSL_PARSE_NAN -#define JSONSL__NAN_PROXY JSONSL_SPECIALf_NAN -#define JSONSL__INF_PROXY JSONSL_SPECIALf_INF -#else -#define JSONSL__NAN_PROXY 0 -#define JSONSL__INF_PROXY 0 -#endif - -/* Stolen from http-parser.h, and possibly others */ -#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) -typedef __int8 int8_t; -typedef unsigned __int8 uint8_t; -typedef __int16 int16_t; -typedef unsigned __int16 uint16_t; -typedef __int32 int32_t; -typedef unsigned __int32 uint32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#if !defined(_MSC_VER) || _MSC_VER<1400 -typedef unsigned int size_t; -typedef int ssize_t; -#endif -#else -#include -#endif - - -#if (!defined(JSONSL_STATE_GENERIC)) && (!defined(JSONSL_STATE_USER_FIELDS)) -#define JSONSL_STATE_GENERIC -#endif /* !defined JSONSL_STATE_GENERIC */ - -#ifdef JSONSL_STATE_GENERIC -#define JSONSL_STATE_USER_FIELDS -#endif /* JSONSL_STATE_GENERIC */ - -/* Additional fields for component object */ -#ifndef JSONSL_JPR_COMPONENT_USER_FIELDS -#define JSONSL_JPR_COMPONENT_USER_FIELDS -#endif - -#ifndef JSONSL_INLINE -#if defined(_MSC_VER) - #define JSONSL_INLINE __inline - #elif defined(__GNUC__) - #define JSONSL_INLINE __inline__ - #else - #define JSONSL_INLINE inline - #endif /* _MSC_VER or __GNUC__ */ -#endif /* JSONSL_INLINE */ - -#define JSONSL_MAX_LEVELS 512 - -struct jsonsl_st; -typedef struct jsonsl_st *jsonsl_t; - -typedef struct jsonsl_jpr_st* jsonsl_jpr_t; - -/** - * This flag is true when AND'd against a type whose value - * must be in "quoutes" i.e. T_HKEY and T_STRING - */ -#define JSONSL_Tf_STRINGY 0xffff00 - -/** - * Constant representing the special JSON types. - * The values are special and aid in speed (the OBJECT and LIST - * values are the char literals of their openings). - * - * Their actual value is a character which attempts to resemble - * some mnemonic reference to the actual type. - * - * If new types are added, they must fit into the ASCII printable - * range (so they should be AND'd with 0x7f and yield something - * meaningful) - */ -#define JSONSL_XTYPE \ - X(STRING, '"'|JSONSL_Tf_STRINGY) \ - X(HKEY, '#'|JSONSL_Tf_STRINGY) \ - X(OBJECT, '{') \ - X(LIST, '[') \ - X(SPECIAL, '^') \ - X(UESCAPE, 'u') -typedef enum { -#define X(o, c) \ - JSONSL_T_##o = c, - JSONSL_XTYPE - JSONSL_T_UNKNOWN = '?', - /* Abstract 'root' object */ - JSONSL_T_ROOT = 0 -#undef X -} jsonsl_type_t; - -/** - * Subtypes for T_SPECIAL. We define them as flags - * because more than one type can be applied to a - * given object. - */ - -#define JSONSL_XSPECIAL \ - X(NONE, 0) \ - X(SIGNED, 1<<0) \ - X(UNSIGNED, 1<<1) \ - X(TRUE, 1<<2) \ - X(FALSE, 1<<3) \ - X(NULL, 1<<4) \ - X(FLOAT, 1<<5) \ - X(EXPONENT, 1<<6) \ - X(NONASCII, 1<<7) \ - X(NAN, 1<<8) \ - X(INF, 1<<9) -typedef enum { -#define X(o,b) \ - JSONSL_SPECIALf_##o = b, - JSONSL_XSPECIAL -#undef X - /* Handy flags for checking */ - - JSONSL_SPECIALf_UNKNOWN = 1 << 10, - - /** @private Private */ - JSONSL_SPECIALf_ZERO = 1 << 11 | JSONSL_SPECIALf_UNSIGNED, - /** @private */ - JSONSL_SPECIALf_DASH = 1 << 12, - /** @private */ - JSONSL_SPECIALf_POS_INF = (JSONSL_SPECIALf_INF), - JSONSL_SPECIALf_NEG_INF = (JSONSL_SPECIALf_INF|JSONSL_SPECIALf_SIGNED), - - /** Type is numeric */ - JSONSL_SPECIALf_NUMERIC = (JSONSL_SPECIALf_SIGNED| JSONSL_SPECIALf_UNSIGNED), - - /** Type is a boolean */ - JSONSL_SPECIALf_BOOLEAN = (JSONSL_SPECIALf_TRUE|JSONSL_SPECIALf_FALSE), - - /** Type is an "extended", not integral type (but numeric) */ - JSONSL_SPECIALf_NUMNOINT = - (JSONSL_SPECIALf_FLOAT|JSONSL_SPECIALf_EXPONENT|JSONSL_SPECIALf_NAN - |JSONSL_SPECIALf_INF) -} jsonsl_special_t; - - -/** - * These are the various types of stack (or other) events - * which will trigger a callback. - * Like the type constants, this are also mnemonic - */ -#define JSONSL_XACTION \ - X(PUSH, '+') \ - X(POP, '-') \ - X(UESCAPE, 'U') \ - X(ERROR, '!') -typedef enum { -#define X(a,c) \ - JSONSL_ACTION_##a = c, - JSONSL_XACTION - JSONSL_ACTION_UNKNOWN = '?' -#undef X -} jsonsl_action_t; - - -/** - * Various errors which may be thrown while parsing JSON - */ -#define JSONSL_XERR \ -/* Trailing garbage characters */ \ - X(GARBAGE_TRAILING) \ -/* We were expecting a 'special' (numeric, true, false, null) */ \ - X(SPECIAL_EXPECTED) \ -/* The 'special' value was incomplete */ \ - X(SPECIAL_INCOMPLETE) \ -/* Found a stray token */ \ - X(STRAY_TOKEN) \ -/* We were expecting a token before this one */ \ - X(MISSING_TOKEN) \ -/* Cannot insert because the container is not ready */ \ - X(CANT_INSERT) \ -/* Found a '\' outside a string */ \ - X(ESCAPE_OUTSIDE_STRING) \ -/* Found a ':' outside of a hash */ \ - X(KEY_OUTSIDE_OBJECT) \ -/* found a string outside of a container */ \ - X(STRING_OUTSIDE_CONTAINER) \ -/* Found a null byte in middle of string */ \ - X(FOUND_NULL_BYTE) \ -/* Current level exceeds limit specified in constructor */ \ - X(LEVELS_EXCEEDED) \ -/* Got a } as a result of an opening [ or vice versa */ \ - X(BRACKET_MISMATCH) \ -/* We expected a key, but got something else instead */ \ - X(HKEY_EXPECTED) \ -/* We got an illegal control character (bad whitespace or something) */ \ - X(WEIRD_WHITESPACE) \ -/* Found a \u-escape, but there were less than 4 following hex digits */ \ - X(UESCAPE_TOOSHORT) \ -/* Invalid two-character escape */ \ - X(ESCAPE_INVALID) \ -/* Trailing comma */ \ - X(TRAILING_COMMA) \ -/* An invalid number was passed in a numeric field */ \ - X(INVALID_NUMBER) \ -/* Value is missing for object */ \ - X(VALUE_EXPECTED) \ -/* The following are for JPR Stuff */ \ - \ -/* Found a literal '%' but it was only followed by a single valid hex digit */ \ - X(PERCENT_BADHEX) \ -/* jsonpointer URI is malformed '/' */ \ - X(JPR_BADPATH) \ -/* Duplicate slash */ \ - X(JPR_DUPSLASH) \ -/* No leading root */ \ - X(JPR_NOROOT) \ -/* Allocation failure */ \ - X(ENOMEM) \ -/* Invalid unicode codepoint detected (in case of escapes) */ \ - X(INVALID_CODEPOINT) - -typedef enum { - JSONSL_ERROR_SUCCESS = 0, -#define X(e) \ - JSONSL_ERROR_##e, - JSONSL_XERR -#undef X - JSONSL_ERROR_GENERIC -} jsonsl_error_t; - - -/** - * A state is a single level of the stack. - * Non-private data (i.e. the 'data' field, see the STATE_GENERIC section) - * will remain in tact until the item is popped. - * - * As a result, it means a parent state object may be accessed from a child - * object, (the parents fields will all be valid). This allows a user to create - * an ad-hoc hierarchy on top of the JSON one. - * - */ -struct jsonsl_state_st { - /** - * The JSON object type - */ - unsigned type; - - /** If this element is special, then its extended type is here */ - unsigned special_flags; - - /** - * The position (in terms of number of bytes since the first call to - * jsonsl_feed()) at which the state was first pushed. This includes - * opening tokens, if applicable. - * - * @note For strings (i.e. type & JSONSL_Tf_STRINGY is nonzero) this will - * be the position of the first quote. - * - * @see jsonsl_st::pos which contains the _current_ position and can be - * used during a POP callback to get the length of the element. - */ - size_t pos_begin; - - /**FIXME: This is redundant as the same information can be derived from - * jsonsl_st::pos at pop-time */ - size_t pos_cur; - - /** - * Level of recursion into nesting. This is mainly a convenience - * variable, as this can technically be deduced from the lexer's - * level parameter (though the logic is not that simple) - */ - unsigned int level; - - - /** - * how many elements in the object/list. - * For objects (hashes), an element is either - * a key or a value. Thus for one complete pair, - * nelem will be 2. - * - * For special types, this will hold the sum of the digits. - * This only holds true for values which are simple signed/unsigned - * numbers. Otherwise a special flag is set, and extra handling is not - * performed. - */ - uint64_t nelem; - - - - /*TODO: merge this and special_flags into a union */ - - - /** - * Useful for an opening nest, this will prevent a callback from being - * invoked on this item or any of its children - */ - int ignore_callback; - - /** - * Counter which is incremented each time an escape ('\') is encountered. - * This is used internally for non-string types and should only be - * inspected by the user if the state actually represents a string - * type. - */ - unsigned int nescapes; - - /** - * Put anything you want here. if JSONSL_STATE_USER_FIELDS is here, then - * the macro expansion happens here. - * - * You can use these fields to store hierarchical or 'tagging' information - * for specific objects. - * - * See the documentation above for the lifetime of the state object (i.e. - * if the private data points to allocated memory, it should be freed - * when the object is popped, as the state object will be re-used) - */ -#ifndef JSONSL_STATE_GENERIC - JSONSL_STATE_USER_FIELDS -#else - - /** - * Otherwise, this is a simple void * pointer for anything you want - */ - void *data; -#endif /* JSONSL_STATE_USER_FIELDS */ -}; - -/**Gets the number of elements in the list. - * @param st The state. Must be of type JSONSL_T_LIST - * @return number of elements in the list - */ -#define JSONSL_LIST_SIZE(st) ((st)->nelem) - -/**Gets the number of key-value pairs in an object - * @param st The state. Must be of type JSONSL_T_OBJECT - * @return the number of key-value pairs in the object - */ -#define JSONSL_OBJECT_SIZE(st) ((st)->nelem / 2) - -/**Gets the numeric value. - * @param st The state. Must be of type JSONSL_T_SPECIAL and - * special_flags must have the JSONSL_SPECIALf_NUMERIC flag - * set. - * @return the numeric value of the state. - */ -#define JSONSL_NUMERIC_VALUE(st) ((st)->nelem) - -/* - * So now we need some special structure for keeping the - * JPR info in sync. Preferably all in a single block - * of memory (there's no need for separate allocations. - * So we will define a 'table' with the following layout - * - * Level nPosbl JPR1_last JPR2_last JPR3_last - * - * 0 1 NOMATCH POSSIBLE POSSIBLE - * 1 0 NOMATCH NOMATCH COMPLETE - * [ table ends here because no further path is possible] - * - * Where the JPR..n corresponds to the number of JPRs - * requested, and nPosble is a quick flag to determine - * - * the number of possibilities. In the future this might - * be made into a proper 'jump' table, - * - * Since we always mark JPRs from the higher levels descending - * into the lower ones, a prospective child match would first - * look at the parent table to check the possibilities, and then - * see which ones were possible.. - * - * Thus, the size of this blob would be (and these are all ints here) - * nLevels * nJPR * 2. - * - * the 'Width' of the table would be nJPR*2, and the 'height' would be - * nlevels - */ - -/** - * This is called when a stack change ocurs. - * - * @param jsn The lexer - * @param action The type of action, this can be PUSH or POP - * @param state A pointer to the stack currently affected by the action - * @param at A pointer to the position of the input buffer which triggered - * this action. - */ -typedef void (*jsonsl_stack_callback)( - jsonsl_t jsn, - jsonsl_action_t action, - struct jsonsl_state_st* state, - const jsonsl_char_t *at); - - -/** - * This is called when an error is encountered. - * Sometimes it's possible to 'erase' characters (by replacing them - * with whitespace). If you think you have corrected the error, you - * can return a true value, in which case the parser will backtrack - * and try again. - * - * @param jsn The lexer - * @param error The error which was thrown - * @param state the current state - * @param a pointer to the position of the input buffer which triggered - * the error. Note that this is not const, this is because you have the - * possibility of modifying the character in an attempt to correct the - * error - * - * @return zero to bail, nonzero to try again (this only makes sense if - * the input buffer has been modified by this callback) - */ -typedef int (*jsonsl_error_callback)( - jsonsl_t jsn, - jsonsl_error_t error, - struct jsonsl_state_st* state, - jsonsl_char_t *at); - -struct jsonsl_st { - /** Public, read-only */ - - /** This is the current level of the stack */ - unsigned int level; - - /** Flag set to indicate we should stop processing */ - unsigned int stopfl; - - /** - * This is the current position, relative to the beginning - * of the stream. - */ - size_t pos; - - /** This is the 'bytes' variable passed to feed() */ - const jsonsl_char_t *base; - - /** Callback invoked for PUSH actions */ - jsonsl_stack_callback action_callback_PUSH; - - /** Callback invoked for POP actions */ - jsonsl_stack_callback action_callback_POP; - - /** Default callback for any action, if neither PUSH or POP callbacks are defined */ - jsonsl_stack_callback action_callback; - - /** - * Do not invoke callbacks for objects deeper than this level. - * NOTE: This field establishes the lower bound for ignored callbacks, - * and is thus misnamed. `min_ignore_level` would actually make more - * sense, but we don't want to break API. - */ - unsigned int max_callback_level; - - /** The error callback. Invoked when an error happens. Should not be NULL */ - jsonsl_error_callback error_callback; - - /* these are boolean flags you can modify. You will be called - * about notification for each of these types if the corresponding - * variable is true. - */ - - /** - * @name Callback Booleans. - * These determine whether a callback is to be invoked for certain types of objects - * @{*/ - - /** Boolean flag to enable or disable the invokcation for events on this type*/ - int call_SPECIAL; - int call_OBJECT; - int call_LIST; - int call_STRING; - int call_HKEY; - /*@}*/ - - /** - * @name u-Escape handling - * Special handling for the \\u-f00d type sequences. These are meant - * to be translated back into the corresponding octet(s). - * A special callback (if set) is invoked with *at=='u'. An application - * may wish to temporarily suspend parsing and handle the 'u-' sequence - * internally (or not). - */ - - /*@{*/ - - /** Callback to be invoked for a u-escape */ - jsonsl_stack_callback action_callback_UESCAPE; - - /** Boolean flag, whether to invoke the callback */ - int call_UESCAPE; - - /** Boolean flag, whether we should return after encountering a u-escape: - * the callback is invoked and then we return if this is true - */ - int return_UESCAPE; - /*@}*/ - - struct { - int allow_trailing_comma; - } options; - - /** Put anything here */ - void *data; - - /*@{*/ - /** Private */ - int in_escape; - char expecting; - char tok_last; - int can_insert; - unsigned int levels_max; - - size_t jpr_count; - jsonsl_jpr_t *jprs; - - /* Root pointer for JPR matching information */ - size_t *jpr_root; - /*@}*/ - -#if defined(_MSC_VER) -#pragma warning (push) -#pragma warning (disable : 4200) -#endif // defined(_MSC_VER) - - /** - * This is the stack. Its upper bound is levels_max, or the - * nlevels argument passed to jsonsl_new. If you modify this structure, - * make sure that this member is last. - */ - struct jsonsl_state_st stack[]; - -#if defined(_MSC_VER) -#pragma warning (pop) -#endif // defined(_MSC_VER) -}; - - -/** - * Creates a new lexer object, with capacity for recursion up to nlevels - * - * @param nlevels maximum recursion depth - */ -jsonsl_t jsonsl_new(int nlevels); - -/** - * Feeds data into the lexer. - * - * @param jsn the lexer object - * @param bytes new data to be fed - * @param nbytes size of new data - */ -void jsonsl_feed(jsonsl_t jsn, const jsonsl_char_t *bytes, size_t nbytes); - -/** - * Resets the internal parser state. This does not free the parser - * but does clean it internally, so that the next time feed() is called, - * it will be treated as a new stream - * - * @param jsn the lexer - */ -void jsonsl_reset(jsonsl_t jsn); - -/** - * Frees the lexer, cleaning any allocated memory taken - * - * @param jsn the lexer - */ -void jsonsl_destroy(jsonsl_t jsn); - -/** - * Gets the 'parent' element, given the current one - * - * @param jsn the lexer - * @param cur the current nest, which should be a struct jsonsl_nest_st - */ -static JSONSL_INLINE -struct jsonsl_state_st *jsonsl_last_state(const jsonsl_t jsn, - const struct jsonsl_state_st *state) -{ - /* Don't complain about overriding array bounds */ - if (state->level > 1) { - return jsn->stack + state->level - 1; - } else { - return NULL; - } -} - -/** - * Gets the state of the last fully consumed child of this parent. This is - * only valid in the parent's POP callback. - * - * @param the lexer - * @return A pointer to the child. - */ -static JSONSL_INLINE -struct jsonsl_state_st *jsonsl_last_child(const jsonsl_t jsn, - const struct jsonsl_state_st *parent) -{ - return jsn->stack + (parent->level + 1); -} - -/**Call to instruct the parser to stop parsing and return. This is valid - * only from within a callback */ -static JSONSL_INLINE -void jsonsl_stop(jsonsl_t jsn) -{ - jsn->stopfl = 1; -} - -/** - * This enables receiving callbacks on all events. Doesn't do - * anything special but helps avoid some boilerplate. - * This does not touch the UESCAPE callbacks or flags. - */ -static JSONSL_INLINE -void jsonsl_enable_all_callbacks(jsonsl_t jsn) -{ - jsn->call_HKEY = 1; - jsn->call_STRING = 1; - jsn->call_OBJECT = 1; - jsn->call_SPECIAL = 1; - jsn->call_LIST = 1; -} - -/** - * A macro which returns true if the current state object can - * have children. This means a list type or an object type. - */ -#define JSONSL_STATE_IS_CONTAINER(state) \ - (state->type == JSONSL_T_OBJECT || state->type == JSONSL_T_LIST) - -/** - * These two functions, dump a string representation - * of the error or type, respectively. They will never - * return NULL - */ -const char* jsonsl_strerror(jsonsl_error_t err); -const char* jsonsl_strtype(jsonsl_type_t jt); - -/** - * @name JSON Pointer API - * - * JSONPointer API. This isn't really related to the lexer (at least not yet) - * JSONPointer provides an extremely simple specification for providing - * locations within JSON objects. We will extend it a bit and allow for - * providing 'wildcard' characters by which to be able to 'query' the stream. - * - * See http://tools.ietf.org/html/draft-pbryan-zyp-json-pointer-00 - * - * Currently I'm implementing the 'single query' API which can only use a single - * query component. In the future I will integrate my yet-to-be-published - * Boyer-Moore-esque prefix searching implementation, in order to allow - * multiple paths to be merged into one for quick and efficient searching. - * - * - * JPR (as we'll refer to it within the source) can be used by splitting - * the components into multiple sections, and incrementally 'track' each - * component. When JSONSL delivers a 'pop' callback for a string, or a 'push' - * callback for an object, we will check to see whether the index matching - * the component corresponding to the current level contains a match - * for our path. - * - * In order to do this properly, a structure must be maintained within the - * parent indicating whether its children are possible matches. This flag - * will be 'inherited' by call children which may conform to the match - * specification, and discarded by all which do not (thereby eliminating - * their children from inheriting it). - * - * A successful match is a complete one. One can provide multiple paths with - * multiple levels of matches e.g. - * /foo/bar/baz/^/blah - * - * @{ - */ - -/** The wildcard character */ -#ifndef JSONSL_PATH_WILDCARD_CHAR -#define JSONSL_PATH_WILDCARD_CHAR '^' -#endif /* WILDCARD_CHAR */ - -#define JSONSL_XMATCH \ - X(COMPLETE,1) \ - X(POSSIBLE,0) \ - X(NOMATCH,-1) \ - X(TYPE_MISMATCH, -2) - -typedef enum { - -#define X(T,v) \ - JSONSL_MATCH_##T = v, - JSONSL_XMATCH - -#undef X - JSONSL_MATCH_UNKNOWN -} jsonsl_jpr_match_t; - -typedef enum { - JSONSL_PATH_STRING = 1, - JSONSL_PATH_WILDCARD, - JSONSL_PATH_NUMERIC, - JSONSL_PATH_ROOT, - - /* Special */ - JSONSL_PATH_INVALID = -1, - JSONSL_PATH_NONE = 0 -} jsonsl_jpr_type_t; - -struct jsonsl_jpr_component_st { - /** The string the component points to */ - char *pstr; - /** if this is a numeric type, the number is 'cached' here */ - unsigned long idx; - /** The length of the string */ - size_t len; - /** The type of component (NUMERIC or STRING) */ - jsonsl_jpr_type_t ptype; - - /** Set this to true to enforce type checking between dict keys and array - * indices. jsonsl_jpr_match() will return TYPE_MISMATCH if it detects - * that an array index is actually a child of a dictionary. */ - short is_arridx; - - /* Extra fields (for more advanced searches. Default is empty) */ - JSONSL_JPR_COMPONENT_USER_FIELDS -}; - -struct jsonsl_jpr_st { - /** Path components */ - struct jsonsl_jpr_component_st *components; - size_t ncomponents; - - /**Type of the match to be expected. If nonzero, will be compared against - * the actual type */ - unsigned match_type; - - /** Base of allocated string for components */ - char *basestr; - - /** The original match string. Useful for returning to the user */ - char *orig; - size_t norig; -}; - -/** - * Create a new JPR object. - * - * @param path the JSONPointer path specification. - * @param errp a pointer to a jsonsl_error_t. If this function returns NULL, - * then more details will be in this variable. - * - * @return a new jsonsl_jpr_t object, or NULL on error. - */ -jsonsl_jpr_t jsonsl_jpr_new(const char *path, jsonsl_error_t *errp); - -/** - * Destroy a JPR object - */ -void jsonsl_jpr_destroy(jsonsl_jpr_t jpr); - -/** - * Match a JSON object against a type and specific level - * - * @param jpr the JPR object - * @param parent_type the type of the parent (should be T_LIST or T_OBJECT) - * @param parent_level the level of the parent - * @param key the 'key' of the child. If the parent is an array, this should be - * empty. - * @param nkey - the length of the key. If the parent is an array (T_LIST), then - * this should be the current index. - * - * NOTE: The key of the child means any kind of associative data related to the - * element. Thus: <<< { "foo" : [ >>, - * the opening array's key is "foo". - * - * @return a status constant. This indicates whether a match was excluded, possible, - * or successful. - */ -jsonsl_jpr_match_t jsonsl_jpr_match(jsonsl_jpr_t jpr, - unsigned int parent_type, - unsigned int parent_level, - const char *key, size_t nkey); - -/** - * Alternate matching algorithm. This matching algorithm does not use - * JSONPointer but relies on a more structured searching mechanism. It - * assumes that there is a clear distinction between array indices and - * object keys. In this case, the jsonsl_path_component_st::ptype should - * be set to @ref JSONSL_PATH_NUMERIC for an array index (the - * jsonsl_path_comonent_st::is_arridx field will be removed in a future - * version). - * - * @param jpr The path - * @param parent The parent structure. Can be NULL if this is the root object - * @param child The child structure. Should not be NULL - * @param key Object key, if an object - * @param nkey Length of object key - * @return Status constant if successful - * - * @note - * For successful matching, both the key and the path itself should be normalized - * to contain 'proper' utf8 sequences rather than utf16 '\uXXXX' escapes. This - * should currently be done in the application. Another version of this function - * may use a temporary buffer in such circumstances (allocated by the application). - * - * Since this function also checks the state of the child, it should only - * be called on PUSH callbacks, and not POP callbacks - */ -jsonsl_jpr_match_t -jsonsl_path_match(jsonsl_jpr_t jpr, - const struct jsonsl_state_st *parent, - const struct jsonsl_state_st *child, - const char *key, size_t nkey); - - -/** - * Associate a set of JPR objects with a lexer instance. - * This should be called before the lexer has been fed any data (and - * behavior is undefined if you don't adhere to this). - * - * After using this function, you may subsequently call match_state() on - * given states (presumably from within the callbacks). - * - * Note that currently the first JPR is the quickest and comes - * pre-allocated with the state structure. Further JPR objects - * are chained. - * - * @param jsn The lexer - * @param jprs An array of jsonsl_jpr_t objects - * @param njprs How many elements in the jprs array. - */ -void jsonsl_jpr_match_state_init(jsonsl_t jsn, - jsonsl_jpr_t *jprs, - size_t njprs); - -/** - * This follows the same semantics as the normal match, - * except we infer parent and type information from the relevant state objects. - * The match status (for all possible JPR objects) is set in the *out parameter. - * - * If a match has succeeded, then its JPR object will be returned. In all other - * instances, NULL is returned; - * - * @param jpr The jsonsl_jpr_t handle - * @param state The jsonsl_state_st which is a candidate - * @param key The hash key (if applicable, can be NULL if parent is list) - * @param nkey Length of hash key (if applicable, can be zero if parent is list) - * @param out A pointer to a jsonsl_jpr_match_t. This will be populated with - * the match result - * - * @return If a match was completed in full, then the JPR object containing - * the matching path will be returned. Otherwise, the return is NULL (note, this - * does not mean matching has failed, it can still be part of the match: check - * the out parameter). - */ -jsonsl_jpr_t jsonsl_jpr_match_state(jsonsl_t jsn, - struct jsonsl_state_st *state, - const char *key, - size_t nkey, - jsonsl_jpr_match_t *out); - - -/** - * Cleanup any memory allocated and any states set by - * match_state_init() and match_state() - * @param jsn The lexer - */ -void jsonsl_jpr_match_state_cleanup(jsonsl_t jsn); - -/** - * Return a string representation of the match result returned by match() - */ -const char *jsonsl_strmatchtype(jsonsl_jpr_match_t match); - -/* @}*/ - -/** - * Utility function to convert escape sequences into their original form. - * - * The decoders I've sampled do not seem to specify a standard behavior of what - * to escape/unescape. - * - * RFC 4627 Mandates only that the quoute, backslash, and ASCII control - * characters (0x00-0x1f) be escaped. It is often common for applications - * to escape a '/' - however this may also be desired behavior. the JSON - * spec is not clear on this, and therefore jsonsl leaves it up to you. - * - * Additionally, sometimes you may wish to _normalize_ JSON. This is specifically - * true when dealing with 'u-escapes' which can be expressed perfectly fine - * as utf8. One use case for normalization is JPR string comparison, in which - * case two effectively equivalent strings may not match because one is using - * u-escapes and the other proper utf8. To normalize u-escapes only, pass in - * an empty `toEscape` table, enabling only the `u` index. - * - * @param in The input string. - * @param out An allocated output (should be the same size as in) - * @param len the size of the buffer - * @param toEscape - A sparse array of characters to unescape. Characters - * which are not present in this array, e.g. toEscape['c'] == 0 will be - * ignored and passed to the output in their original form. - * @param oflags If not null, and a \uXXXX escape expands to a non-ascii byte, - * then this variable will have the SPECIALf_NONASCII flag on. - * - * @param err A pointer to an error variable. If an error ocurrs, it will be - * set in this variable - * @param errat If not null and an error occurs, this will be set to point - * to the position within the string at which the offending character was - * encountered. - * - * @return The effective size of the output buffer. - * - * @note - * This function now encodes the UTF8 equivalents of utf16 escapes (i.e. - * 'u-escapes'). Previously this would encode the escapes as utf16 literals, - * which while still correct in some sense was confusing for many (especially - * considering that the inputs were variations of char). - * - * @note - * The output buffer will never be larger than the input buffer, since - * standard escape sequences (i.e. '\t') occupy two bytes in the source - * but only one byte (when unescaped) in the output. Likewise u-escapes - * (i.e. \uXXXX) will occupy six bytes in the source, but at the most - * two bytes when escaped. - */ -size_t jsonsl_util_unescape_ex(const char *in, - char *out, - size_t len, - const int toEscape[128], - unsigned *oflags, - jsonsl_error_t *err, - const char **errat); - -/** - * Convenience macro to avoid passing too many parameters - */ -#define jsonsl_util_unescape(in, out, len, toEscape, err) \ - jsonsl_util_unescape_ex(in, out, len, toEscape, NULL, err, NULL) - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* JSONSL_H_ */ diff --git a/build-wheels.sh b/build-wheels.sh deleted file mode 100755 index e5d1e02..0000000 --- a/build-wheels.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash -ex - -if [ "$#" -ne 1 ]; then - echo "$0 requires one argument: " - echo "For example: $0 /user/home/git/python-bsonjs" - exit 1 -fi - -BSONJS_SOURCE_DIRECTORY="$1" -cd "$BSONJS_SOURCE_DIRECTORY" - -ls -la -if [ -z "$PYTHON_BINARY" ]; then - PYTHON_BINARY="python" -fi - -$PYTHON_BINARY --version - -if [ ! "$(uname)" == "Linux" ]; then - $PYTHON_BINARY -m pip install wheel -fi -# Build limited abi3 wheel. -$PYTHON_BINARY setup.py bdist_wheel -# https://github.com/pypa/manylinux/issues/49 -rm -rf build - -# Audit wheels and write multilinux1 tag -# Only if on linux -if [ "$(uname)" == "Linux" ]; then - for whl in dist/*.whl; do - # Skip already built manylinux wheels. - if [[ "$whl" != *"manylinux"* ]]; then - auditwheel repair $whl -w dist - rm $whl - fi - done -fi - -# Install packages and test. -for PYBIN in /opt/python/*/bin; do - if [[ ! "${PYBIN}" =~ (39|310) || "${PYBIN}" =~ (pypy) ]]; then - continue - fi - "${PYBIN}/pip" install python-bsonjs --no-index -f dist - # The tests require PyMongo. - "${PYBIN}/pip" install 'pymongo>=4' - for TEST_FILE in "${BSONJS_SOURCE_DIRECTORY}"/test/test_*.py; do - "${PYBIN}/python" "$TEST_FILE" -v - done -done - -ls -lah dist diff --git a/docker-build.sh b/docker-build.sh deleted file mode 100755 index 2cc3ecc..0000000 --- a/docker-build.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -if [ "$#" -ne 0 ]; then - echo "$0 takes no arguments" - exit 1 -fi - -set -e -x - - -DOCKER_IMAGE=quay.io/pypa/manylinux1_x86_64 -docker pull "$DOCKER_IMAGE" -docker run --rm -v `pwd`:/io "$DOCKER_IMAGE" /io/build-wheels.sh /io - -DOCKER_IMAGE=quay.io/pypa/manylinux1_i686 -docker pull "$DOCKER_IMAGE" -docker run --rm -v `pwd`:/io "$DOCKER_IMAGE" linux32 /io/build-wheels.sh /io diff --git a/vendor.sh b/vendor.sh deleted file mode 100644 index 962a32f..0000000 --- a/vendor.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -set -eu -VERSION="2.5.0" -rm -rf mongo-c-driver -git clone git@github.com:mongodb/mongo-c-driver.git -pushd mongo-c-driver -git checkout $VERSION -mkdir cmake-build && cd cmake-build -cmake -DENABLE_MONGOC=OFF .. -popd -rm -r bsonjs/bson -rm -r bsonjs/jsonsl -rm -r bsonjs/common -rsync -r mongo-c-driver/src/libbson/src/bson/*.[hc] bsonjs/bson/ -rsync -r mongo-c-driver/src/libbson/src/jsonsl/*.[hc] bsonjs/jsonsl/ -rsync -r mongo-c-driver/src/libbson/src/jsonsl/LICENSE bsonjs/jsonsl/ - -rsync -r mongo-c-driver/src/common/src/*.[hc] bsonjs/common/ -rsync -r mongo-c-driver/cmake-build/src/common/src/*.[hc] bsonjs/common/ -mkdir -p bsonjs/common/mlib -rsync -r mongo-c-driver/src/common/src/mlib/*.h mongo-c-driver/src/common/src/mlib/*.th bsonjs/common/mlib/ - -rsync -r mongo-c-driver/cmake-build/src/libbson/src/bson/*.[hc] bsonjs/bson/ - -# Drop implementations for libbson features bsonjs doesn't use and that -# nothing else in the vendored tree calls into (BCON, MD5, the thread -# helpers). Their headers stay: bson.h includes bson-bcon.h, and -# common-thread-private.h is a real dependency of bson-context.c and -# common-b64.c. -rm -f bsonjs/bson/bson-bcon.c -rm -f bsonjs/common/common-md5.c -rm -f bsonjs/common/common-thread.c - -# Ignore autogenerated config.h and common-config.h -git diff -- bsonjs/bson/config.h | tee -echo "**** Review libbson's autogenerated src/bson/config.h (above) for newly added (or removed) macros ****" -git checkout -- bsonjs/bson/config.h - -git diff -- bsonjs/common/common-config.h | tee -echo "**** Review libbson's autogenerated src/common/common-config.h (above) for newly added (or removed) macros ****" -git checkout -- bsonjs/common/common-config.h From a312847dfe34ed25d98c9090ac233f91671edad4 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 14:59:23 -0500 Subject: [PATCH 22/69] Ignore libbson build artifacts --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b2c9eb3..360c87e 100644 --- a/.gitignore +++ b/.gitignore @@ -132,4 +132,8 @@ ENV/ .idea/** # Local checkout of mongo-c-driver -mongo-c-driver/ \ No newline at end of file +mongo-c-driver/ + +# libbson build artifacts +libbson/ +mongo-c-driver-*/ \ No newline at end of file From 9886831e3564cedb6832dc499a17e01f0d8c00c8 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 14:59:40 -0500 Subject: [PATCH 23/69] Update CI workflows for build-time libbson checkout --- .github/workflows/codeql.yml | 23 ++++++++--------------- .github/workflows/test.yml | 4 ++++ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2230290..5424d97 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -26,20 +26,18 @@ jobs: include: - language: c-cpp build-mode: manual - # Compile only our own bsonjs.c, not the vendored libbson/jsonsl - # sources under bsonjs/{bson,jsonsl,common}. CodeQL's tracer - # only extracts files the compiler is actually invoked on, and - # for build-mode: manual, paths-ignore below does not filter - # results (that only works for interpreted languages or - # build-mode: none) — so keeping vendored code out of scope - # means never compiling it here. + # Build libbson from source, then compile only our own bsonjs.c + # against the installed headers. CodeQL's tracer only extracts + # files the compiler is actually invoked on, so keeping libbson + # itself out of scope means never compiling it here. manual-build-command: | + bash build-libbson.sh PYINC=$(python3 -c "import sysconfig; print(sysconfig.get_paths()['include'])") BSONJS_VERSION=$(python3 -c "import re; print(re.search(r'(?m)^version\s*=\s*\"([^\"]+)\"', open('pyproject.toml').read()).group(1))") + LIBBSON_INCLUDE=$(ls -d libbson/include/bson-*/bson) gcc -c bsonjs/bsonjs.c -o /tmp/bsonjs.o \ - -Ibsonjs -Ibsonjs/bson -Ibsonjs/jsonsl -Ibsonjs/common -I"$PYINC" \ - -DBSON_COMPILATION=1 -DPy_LIMITED_API=0x03090000 "-DBSONJS_VERSION=\"$BSONJS_VERSION\"" \ - -D_XOPEN_SOURCE=700 -D_BSD_SOURCE=1 -D_DEFAULT_SOURCE=1 + -Ibsonjs -I"$LIBBSON_INCLUDE" -I"$PYINC" \ + -DBSON_STATIC -DPy_LIMITED_API=0x03090000 "-DBSONJS_VERSION=\"$BSONJS_VERSION\"" - language: python build-mode: none - language: actions @@ -52,9 +50,4 @@ jobs: manual-build-command: ${{ matrix.manual-build-command }} config: | paths-ignore: - # Vendored libbson/jsonsl sources, mechanically synced by - # vendor.sh — not ours to fix findings in. - - 'bsonjs/bson/**' - - 'bsonjs/jsonsl/**' - - 'bsonjs/common/**' - 'test/**' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 16440cb..b74476d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,7 +28,11 @@ jobs: with: python-version: ${{ matrix.python-version }} allow-prereleases: true + - name: Build libbson + run: bash build-libbson.sh - name: Test with python run: | python -m pip install -v -e ".[test]" pytest + env: + LIBBSON_INSTALL_DIR: ${{ github.workspace }}/libbson From cdace52b8ae4c49fddb2750e498d0b6326980b06 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 15:00:36 -0500 Subject: [PATCH 24/69] Set both libbson include dirs so #include resolves --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 979a02e..ec150d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ if(NOT _bson_include_dirs) message(FATAL_ERROR "Could not find bson headers under ${LIBBSON_INSTALL_DIR}/include") endif() list(GET _bson_include_dirs 0 LIBBSON_INCLUDE_DIR) +get_filename_component(LIBBSON_VERSION_DIR "${LIBBSON_INCLUDE_DIR}" DIRECTORY) # Find static library if(WIN32) @@ -60,6 +61,7 @@ Python_add_library(bsonjs MODULE WITH_SOABI "${CMAKE_CURRENT_SOURCE_DIR}/bsonjs/ target_include_directories(bsonjs PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/bsonjs" "${LIBBSON_INCLUDE_DIR}" + "${LIBBSON_VERSION_DIR}" ) target_compile_definitions(bsonjs PRIVATE From e302ffceffa31f76842dc867811894732aa68c30 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 15:00:51 -0500 Subject: [PATCH 25/69] Update docs for build-time libbson checkout --- CHANGELOG.rst | 3 ++- README.rst | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c50f2fa..1d7662a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,7 +3,8 @@ Changelog 0.8.0 ````` -Version 0.8.0 updates python-bsonjs's vendored copy of libbson to 2.5.0. +Version 0.8.0 builds libbson 2.5.0 from source at install time instead of +vendoring C sources. Building from source requires CMake 3.17+ and git. For a detailed breakdown of what changed in each version of libbson see its changelog: https://github.com/mongodb/mongo-c-driver/blob/2.5.0/NEWS http://mongoc.org/libbson/2.5.0/ diff --git a/README.rst b/README.rst index 467744b..94801a3 100644 --- a/README.rst +++ b/README.rst @@ -18,6 +18,8 @@ python-bsonjs can be installed with `pip `_:: $ python -m pip install python-bsonjs +Building from source requires CMake 3.17+ and git (to fetch libbson). + Examples ======== From a2ed673ee467c3ece5bb73c21372fbb0b1a540ec Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 17:12:08 -0500 Subject: [PATCH 26/69] Run CodeQL on the master branch --- .github/workflows/codeql.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 5424d97..04ea7ec 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,7 +2,7 @@ name: "CodeQL" on: push: - branches: ["main"] + branches: ["master"] pull_request: schedule: - cron: '17 10 * * 2' From e7acce10f9edd08f6f4b2ba4979576ff448436f1 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 19:30:43 -0500 Subject: [PATCH 27/69] Build libbson via CMake FetchContent instead of an install-time script --- .github/workflows/codeql.yml | 24 +++++++---- .github/workflows/test.yml | 4 -- .gitignore | 4 -- CHANGELOG.rst | 2 +- CMakeLists.txt | 78 ++++++++++++++---------------------- README.rst | 2 +- build-libbson.sh | 53 ------------------------ cibw_before_build.sh | 18 --------- pyproject.toml | 4 -- 9 files changed, 48 insertions(+), 141 deletions(-) delete mode 100755 build-libbson.sh delete mode 100755 cibw_before_build.sh diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 04ea7ec..2435694 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -26,17 +26,27 @@ jobs: include: - language: c-cpp build-mode: manual - # Build libbson from source, then compile only our own bsonjs.c - # against the installed headers. CodeQL's tracer only extracts - # files the compiler is actually invoked on, so keeping libbson - # itself out of scope means never compiling it here. + # Configure the project so CMake fetches mongo-c-driver and + # generates libbson's config.h/version.h. Then compile only our own + # bsonjs.c against that tree. CodeQL's tracer only extracts files the + # compiler is actually invoked on, so keeping libbson itself out of + # scope means never compiling it here. manual-build-command: | - bash build-libbson.sh + if ! command -v cmake >/dev/null 2>&1; then pip install "cmake>=3.17,<4"; fi + cmake -S . -B /tmp/codeql-build \ + -DENABLE_MONGOC=OFF -DENABLE_SHARED=OFF -DENABLE_STATIC=ON \ + -DENABLE_TESTS=OFF -DBUILD_TESTING=OFF \ + -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF \ + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ + -DCMAKE_BUILD_TYPE=Release PYINC=$(python3 -c "import sysconfig; print(sysconfig.get_paths()['include'])") BSONJS_VERSION=$(python3 -c "import re; print(re.search(r'(?m)^version\s*=\s*\"([^\"]+)\"', open('pyproject.toml').read()).group(1))") - LIBBSON_INCLUDE=$(ls -d libbson/include/bson-*/bson) + SRC=/tmp/codeql-build/_deps/mongo-c-driver-src + BIN=/tmp/codeql-build/_deps/mongo-c-driver-build gcc -c bsonjs/bsonjs.c -o /tmp/bsonjs.o \ - -Ibsonjs -I"$LIBBSON_INCLUDE" -I"$PYINC" \ + -Ibsonjs -I"$SRC/src/libbson/src" -I"$SRC/src/libbson/src/bson" \ + -I"$BIN/src/libbson/src" -I"$SRC/src/common/src" -I"$BIN/src/common/src" \ + -I"$PYINC" \ -DBSON_STATIC -DPy_LIMITED_API=0x03090000 "-DBSONJS_VERSION=\"$BSONJS_VERSION\"" - language: python build-mode: none diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b74476d..16440cb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,11 +28,7 @@ jobs: with: python-version: ${{ matrix.python-version }} allow-prereleases: true - - name: Build libbson - run: bash build-libbson.sh - name: Test with python run: | python -m pip install -v -e ".[test]" pytest - env: - LIBBSON_INSTALL_DIR: ${{ github.workspace }}/libbson diff --git a/.gitignore b/.gitignore index 360c87e..663c87a 100644 --- a/.gitignore +++ b/.gitignore @@ -133,7 +133,3 @@ ENV/ # Local checkout of mongo-c-driver mongo-c-driver/ - -# libbson build artifacts -libbson/ -mongo-c-driver-*/ \ No newline at end of file diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1d7662a..859b53d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,7 +4,7 @@ Changelog 0.8.0 ````` Version 0.8.0 builds libbson 2.5.0 from source at install time instead of -vendoring C sources. Building from source requires CMake 3.17+ and git. +vendoring C sources. Building from source requires CMake 3.17+. For a detailed breakdown of what changed in each version of libbson see its changelog: https://github.com/mongodb/mongo-c-driver/blob/2.5.0/NEWS http://mongoc.org/libbson/2.5.0/ diff --git a/CMakeLists.txt b/CMakeLists.txt index ec150d2..34ef921 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,60 +8,40 @@ file(READ "${CMAKE_CURRENT_SOURCE_DIR}/pyproject.toml" _pyproject) string(REGEX MATCH "version = \"([^\"]*)\"" _ "${_pyproject}") set(_package_version "${CMAKE_MATCH_1}") -# --- libbson static library detection --- -set(LIBBSON_INSTALL_DIR "$ENV{LIBBSON_INSTALL_DIR}") -if(NOT LIBBSON_INSTALL_DIR) - set(LIBBSON_INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/libbson") -endif() -get_filename_component(LIBBSON_INSTALL_DIR "${LIBBSON_INSTALL_DIR}" ABSOLUTE) - -# Auto-build libbson if the install dir does not contain a lib directory. -if(NOT EXISTS "${LIBBSON_INSTALL_DIR}/lib") - message(STATUS "libbson not found, building from source...") - execute_process( - COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/build-libbson.sh - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - RESULT_VARIABLE _build_result - ) - if(NOT _build_result EQUAL 0) - message(FATAL_ERROR "Failed to build libbson via build-libbson.sh") - endif() -endif() +# --- libbson static library via FetchContent --- +include(FetchContent) -# Find headers: libbson 2.x installs to include/bson-X.Y.Z/bson/ -file(GLOB _bson_include_dirs "${LIBBSON_INSTALL_DIR}/include/bson-*/bson") -if(NOT _bson_include_dirs) - message(FATAL_ERROR "Could not find bson headers under ${LIBBSON_INSTALL_DIR}/include") -endif() -list(GET _bson_include_dirs 0 LIBBSON_INCLUDE_DIR) -get_filename_component(LIBBSON_VERSION_DIR "${LIBBSON_INCLUDE_DIR}" DIRECTORY) +FetchContent_Declare( + mongo-c-driver + URL https://github.com/mongodb/mongo-c-driver/archive/refs/tags/2.5.0.tar.gz + DOWNLOAD_EXTRACT_TIMESTAMP TRUE +) -# Find static library -if(WIN32) - file(GLOB _bson_libs - "${LIBBSON_INSTALL_DIR}/lib/bson2-static.lib" - "${LIBBSON_INSTALL_DIR}/lib/bson2.lib" - "${LIBBSON_INSTALL_DIR}/lib/*/bson2-static.lib" - "${LIBBSON_INSTALL_DIR}/lib/*/bson2.lib" - ) -else() - file(GLOB _bson_libs - "${LIBBSON_INSTALL_DIR}/lib/libbson2.a" - "${LIBBSON_INSTALL_DIR}/lib*/libbson2.a" - ) -endif() -if(NOT _bson_libs) - message(FATAL_ERROR "Could not find libbson2.a static library under ${LIBBSON_INSTALL_DIR}/lib") -endif() -list(GET _bson_libs 0 LIBBSON_STATIC_LIB) +# The driver builds a C++ test gated on BUILD_TESTING; keep the build C-only. +set(BUILD_TESTING OFF) +set(ENABLE_TESTS OFF) +FetchContent_MakeAvailable(mongo-c-driver) + +# bson:static (alias bson_static) propagates BSON_STATIC already, but the +# driver does not export its include dirs on the target, so add them here so +# both #include and #include resolve. The generated +# config.h/version.h live in the binary dir, and the common/mlib headers come +# from the common tree. +set(_bson_src "${mongo-c-driver_SOURCE_DIR}/src/libbson") +set(_bson_bin "${mongo-c-driver_BINARY_DIR}/src/libbson") +set(_common_src "${mongo-c-driver_SOURCE_DIR}/src/common") +set(_common_bin "${mongo-c-driver_BINARY_DIR}/src/common") # --- Build the extension module --- Python_add_library(bsonjs MODULE WITH_SOABI "${CMAKE_CURRENT_SOURCE_DIR}/bsonjs/bsonjs.c") target_include_directories(bsonjs PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/bsonjs" - "${LIBBSON_INCLUDE_DIR}" - "${LIBBSON_VERSION_DIR}" + "${_bson_src}/src" + "${_bson_src}/src/bson" + "${_bson_bin}/src" + "${_common_src}/src" + "${_common_bin}/src" ) target_compile_definitions(bsonjs PRIVATE @@ -71,11 +51,11 @@ target_compile_definitions(bsonjs PRIVATE ) if(WIN32) - target_link_libraries(bsonjs PRIVATE ${LIBBSON_STATIC_LIB} ws2_32) + target_link_libraries(bsonjs PRIVATE bson_static ws2_32) elseif(APPLE) - target_link_libraries(bsonjs PRIVATE ${LIBBSON_STATIC_LIB}) + target_link_libraries(bsonjs PRIVATE bson_static) else() - target_link_libraries(bsonjs PRIVATE ${LIBBSON_STATIC_LIB} rt) + target_link_libraries(bsonjs PRIVATE bson_static rt) endif() install(TARGETS bsonjs DESTINATION .) diff --git a/README.rst b/README.rst index 94801a3..425321d 100644 --- a/README.rst +++ b/README.rst @@ -18,7 +18,7 @@ python-bsonjs can be installed with `pip `_:: $ python -m pip install python-bsonjs -Building from source requires CMake 3.17+ and git (to fetch libbson). +Building from source requires CMake 3.17+ (to download and build libbson). Examples ======== diff --git a/build-libbson.sh b/build-libbson.sh deleted file mode 100755 index 40e3b37..0000000 --- a/build-libbson.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash -ex - -set -o xtrace -set -o errexit - -# Version of libbson to build. -LIBBSON_VERSION=${LIBBSON_VERSION:-"2.5.0"} -if [ -z "$LIBBSON_VERSION" ] -then - echo "Did not provide a libbson revision ID to build" - exit 1 -fi - -# Setup working directory -WORKDIR="mongo-c-driver-${LIBBSON_VERSION}" -if [ ! -d "$WORKDIR" ] -then - git clone --depth 1 -b "$LIBBSON_VERSION" https://github.com/mongodb/mongo-c-driver.git "$WORKDIR" -fi - -DEFAULT_ARCH=$(uname -m) -MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET:-"10.15"} -CMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES:-${DEFAULT_ARCH}} -CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-"Release"} - -DEFAULT_INSTALL_DIR=$(pwd)/libbson -LIBBSON_INSTALL_DIR=${LIBBSON_INSTALL_DIR:-${DEFAULT_INSTALL_DIR}} -LIBBSON_INSTALL_DIR="$(cd "$(dirname "$LIBBSON_INSTALL_DIR")"; pwd)/$(basename "$LIBBSON_INSTALL_DIR")" - -echo "MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET}" -echo "CMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES}" -echo "LIBBSON_INSTALL_DIR=${LIBBSON_INSTALL_DIR}" - -pushd "$WORKDIR" - git checkout "$LIBBSON_VERSION" - mkdir -p cmake-build - pushd cmake-build - cmake -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF \ - -DENABLE_MONGOC=OFF \ - -DENABLE_SHARED=OFF \ - -DENABLE_STATIC=ON \ - -DBUILD_SHARED_LIBS=OFF \ - -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ - -DCMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} \ - -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} \ - -DCMAKE_INSTALL_PREFIX:PATH="$LIBBSON_INSTALL_DIR" \ - .. - cmake --build . --target clean - cmake --build . - cmake --build . --target install --config ${CMAKE_BUILD_TYPE} - popd -popd diff --git a/cibw_before_build.sh b/cibw_before_build.sh deleted file mode 100755 index cc8558f..0000000 --- a/cibw_before_build.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -ex - -# Install cmake if the runner does not provide a compatible version. -if ! command -v cmake >/dev/null 2>&1; then - pip install "cmake>=3.17,<4" -fi - -# Set macOS architecture from cibuildwheel. -if [[ "$CIBW_BUILD" == *"macosx_"* ]]; then - if [[ "$ARCHFLAGS" == *"arm64"* ]]; then - export CMAKE_OSX_ARCHITECTURES="arm64" - else - export CMAKE_OSX_ARCHITECTURES="x86_64" - fi - export MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET:-"10.15"} -fi - -CMAKE_BUILD_TYPE=Release bash ./build-libbson.sh diff --git a/pyproject.toml b/pyproject.toml index 3838d8e..ed2ce34 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,10 +49,6 @@ wheel.py-api = "cp39" test-command = "pytest {package}/test" test-extras = ["test"] skip = ["cp314t-*"] -before-build = "bash ./cibw_before_build.sh" - -[tool.cibuildwheel.environment] -LIBBSON_INSTALL_DIR = "./libbson" # Use abi3audit to catch issues with Limited API wheels [tool.cibuildwheel.linux] From 70913a7e0ef66e1b1c92d8483611118a46b1bd5b Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 19:36:39 -0500 Subject: [PATCH 28/69] Add bump-libbson.sh to bump libbson and refresh README benchmark --- README.rst | 18 +++--- bump-libbson.sh | 163 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 9 deletions(-) create mode 100755 bump-libbson.sh diff --git a/README.rst b/README.rst index 425321d..71420c1 100644 --- a/README.rst +++ b/README.rst @@ -57,22 +57,22 @@ Using bsonjs with pymongo to insert a RawBSONDocument. Speed ===== -bsonjs is roughly 5-17x faster than PyMongo's json_util at decoding BSON to -JSON and encoding JSON to BSON. See `benchmark.py`:: +bsonjs is roughly 3-9x faster than PyMongo 4.18.1's +json_util at decoding BSON to JSON and encoding JSON to BSON. Benchmarked +against libbson 2.5.0. See `benchmark.py`:: $ python benchmark.py Timing: bsonjs.dumps(b) - 10000 loops, best of 3: 0.028451045000110753 + 10000 loops, best of 3: 0.02494637500785757 Timing: json_util.dumps(bson.decode(b)) - 10000 loops, best of 3: 0.47029594800005725 - bsonjs is 16.53x faster than json_util + 10000 loops, best of 3: 0.23053841599903535 + bsonjs is 9.24x faster than json_util Timing: bsonjs.loads(j) - 10000 loops, best of 3: 0.08982307400015088 + 10000 loops, best of 3: 0.06266883299394976 Timing: bson.encode(json_util.loads(j)) - 10000 loops, best of 3: 0.4777698939999482 - bsonjs is 5.32x faster than json_util - + 10000 loops, best of 3: 0.2060290410008747 + bsonjs is 3.29x faster than json_util Limitations =========== diff --git a/bump-libbson.sh b/bump-libbson.sh new file mode 100755 index 0000000..f12f800 --- /dev/null +++ b/bump-libbson.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env bash +set -eu + +# Usage: bump-libbson.sh [LIBBSON_VERSION] +# Defaults to the latest released mongo-c-driver tag (libbson version). +LIBBSON_VERSION=${1:-"2.5.0"} + +# 1. Update the libbson version in CMakeLists.txt (the FetchContent URL), +# the README About line, and the CHANGELOG 0.8.0 entry. +python3 - "$LIBBSON_VERSION" <<'PY' +import re +import sys + +version = sys.argv[1] + + +def sub_file(path, pattern, repl, label): + with open(path) as f: + text = f.read() + if not re.search(pattern, text): + raise SystemExit( + "Could not update {} ({} not found)".format(path, label) + ) + new = re.sub(pattern, repl.format(version), text) + with open(path, "w") as f: + f.write(new) + + +sub_file( + "CMakeLists.txt", + r"refs/tags/[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz", + "refs/tags/{}.tar.gz", + "FetchContent URL", +) +# README About link: http://mongoc.org/libbson// +sub_file( + "README.rst", + r"(mongoc\.org/libbson/)[0-9]+\.[0-9]+\.[0-9]+/", + r"\g<1>{}/", + "README About link", +) +# CHANGELOG 0.8.0 entry: the libbson mention and the two URLs. Scope +# the edits to the 0.8.0 section only so historic entries are left alone. +with open("CHANGELOG.rst") as f: + changelog = f.read() + +sec = re.search(r"(?ms)^0\.8\.0\s*\n\s*```+\s*\n.*?(?=\n0\.7\.0)", changelog) +if not sec: + raise SystemExit("Could not find the CHANGELOG 0.8.0 section") +block = sec.group(0) +new_block = re.sub( + r"libbson [0-9]+\.[0-9]+\.[0-9]+ from source", + "libbson {} from source".format(version), + block, +) +new_block = re.sub( + r"mongo-c-driver/blob/[0-9]+\.[0-9]+\.[0-9]+/NEWS", + "mongo-c-driver/blob/{}/NEWS".format(version), + new_block, +) +new_block = re.sub( + r"mongoc\.org/libbson/[0-9]+\.[0-9]+\.[0-9]+/", + "mongoc.org/libbson/{}/".format(version), + new_block, +) +if not any( + re.search(p, block) + for p in ( + r"libbson [0-9]+\.[0-9]+\.[0-9]+ from source", + r"mongo-c-driver/blob/[0-9]+\.[0-9]+\.[0-9]+/NEWS", + r"mongoc\.org/libbson/[0-9]+\.[0-9]+\.[0-9]+/", + ) +): + raise SystemExit("Could not update the CHANGELOG 0.8.0 entry") +changelog = changelog[:sec.start()] + new_block + changelog[sec.end():] +with open("CHANGELOG.rst", "w") as f: + f.write(changelog) +PY + +echo "Updated libbson version to: ${LIBBSON_VERSION}" + +# 2. Install the package and the latest stable pymongo. +python3 -m pip install -e ".[test]" +python3 -m pip install --upgrade "pymongo>=4" + +# 3. Run the benchmark, capturing raw output. +BENCHMARK_OUT=$(mktemp) +python3 benchmark.py > "$BENCHMARK_OUT" 2>&1 +echo "Benchmark:" +sed 's/^/ /' "$BENCHMARK_OUT" + +# 4. Update the README Speed section with the results and versions. +PYMONGO_VERSION=$(python3 -c "import pymongo; print(pymongo.version)") +python3 - "$LIBBSON_VERSION" "$PYMONGO_VERSION" "$BENCHMARK_OUT" <<'PY' +import re +import sys + +libbson_version, pymongo_version, bench_path = sys.argv[1], sys.argv[2], sys.argv[3] +readme_path = "README.rst" + +with open(bench_path) as f: + bench = f.read() + +ratios = [ + float(x) + for x in re.findall(r"bsonjs is ([0-9.]+?)x faster", bench) +] +if len(ratios) != 2: + raise SystemExit("Expected two benchmark ratios, got: {}".format(ratios)) + +lo, hi = min(ratios), max(ratios) + +raw_numbers = re.findall(r"best of 3: ([0-9.e+-]+)", bench) +if len(raw_numbers) != 4: + raise SystemExit("Expected four benchmark timings, got: {}".format(raw_numbers)) + +dumps_bsonjs, dumps_json_util, loads_bsonjs, loads_json_util = raw_numbers + +new_block = """Speed +===== + +bsonjs is roughly {lo:.0f}-{hi:.0f}x faster than PyMongo {pymongo_version}'s +json_util at decoding BSON to JSON and encoding JSON to BSON. Benchmarked +against libbson {libbson_version}. See `benchmark.py`:: + + $ python benchmark.py + Timing: bsonjs.dumps(b) + 10000 loops, best of 3: {dumps_bsonjs} + Timing: json_util.dumps(bson.decode(b)) + 10000 loops, best of 3: {dumps_json_util} + bsonjs is {dumps_ratio:.2f}x faster than json_util + + Timing: bsonjs.loads(j) + 10000 loops, best of 3: {loads_bsonjs} + Timing: bson.encode(json_util.loads(j)) + 10000 loops, best of 3: {loads_json_util} + bsonjs is {loads_ratio:.2f}x faster than json_util +""".format( + lo=lo, + hi=hi, + pymongo_version=pymongo_version, + libbson_version=libbson_version, + dumps_bsonjs=dumps_bsonjs, + dumps_json_util=dumps_json_util, + dumps_ratio=ratios[0], + loads_bsonjs=loads_bsonjs, + loads_json_util=loads_json_util, + loads_ratio=ratios[1], +) + +with open(readme_path) as f: + readme = f.read() + +speedy = re.compile(r"Speed\n=====\n\n.*?(?=\nLimitations)", re.DOTALL) +if not speedy.search(readme): + raise SystemExit("Could not find the Speed section in README.rst") +readme = speedy.sub(new_block, readme, count=1) + +with open(readme_path, "w") as f: + f.write(readme) +PY + +echo "Updated README.rst (libbson ${LIBBSON_VERSION}, pymongo ${PYMONGO_VERSION})." From ea1f780190779481751d1a9452f925b55096a1c4 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 19:38:14 -0500 Subject: [PATCH 29/69] Detect latest mongo-c-driver and bail when libbson is current --- bump-libbson.sh | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/bump-libbson.sh b/bump-libbson.sh index f12f800..872866f 100755 --- a/bump-libbson.sh +++ b/bump-libbson.sh @@ -2,8 +2,47 @@ set -eu # Usage: bump-libbson.sh [LIBBSON_VERSION] -# Defaults to the latest released mongo-c-driver tag (libbson version). -LIBBSON_VERSION=${1:-"2.5.0"} +# With no argument, fetches the latest released mongo-c-driver tag. If the +# repo already pins that version, prints that it is up to date and exits +# without installing or benchmarking. + +# --- Determine the libbson version to bump to --- +CURRENT_VERSION=$(python3 - <<'PY' +import re +with open("CMakeLists.txt") as f: + text = f.read() +m = re.search(r"refs/tags/([0-9]+\.[0-9]+\.[0-9]+)\.tar\.gz", text) +if not m: + raise SystemExit("Could not read the libbson version from CMakeLists.txt") +print(m.group(1)) +PY +) + +LATEST_VERSION=$(python3 - <<'PY' +import json +import sys +import urllib.request + +url = "https://api.github.com/repos/mongodb/mongo-c-driver/releases/latest" +try: + with urllib.request.urlopen(url, timeout=30) as resp: + data = json.load(resp) +except Exception as exc: + raise SystemExit("Could not fetch the latest mongo-c-driver release: {}".format(exc)) +print(data["tag_name"].lstrip("v")) +PY +) + +if [ -z "${1:-}" ]; then + LIBBSON_VERSION="$LATEST_VERSION" + if [ "$LIBBSON_VERSION" == "$CURRENT_VERSION" ]; then + echo "libbson is already up to date (${CURRENT_VERSION})." + exit 0 + fi + echo "Found latest libbson ${LATEST_VERSION}; current is ${CURRENT_VERSION}." +else + LIBBSON_VERSION="$1" +fi # 1. Update the libbson version in CMakeLists.txt (the FetchContent URL), # the README About line, and the CHANGELOG 0.8.0 entry. From f3d7ce66332b9d902186772b78b502d0ef6c0a52 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 19:42:38 -0500 Subject: [PATCH 30/69] Update README for FetchContent build and bump libbson to 2.5.3 --- CHANGELOG.rst | 6 +++--- CMakeLists.txt | 2 +- README.rst | 37 ++++++++++++++++++++++++++----------- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 859b53d..6e827e2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,11 +3,11 @@ Changelog 0.8.0 ````` -Version 0.8.0 builds libbson 2.5.0 from source at install time instead of +Version 0.8.0 builds libbson 2.5.3 from source at install time instead of vendoring C sources. Building from source requires CMake 3.17+. For a detailed breakdown of what changed in each version of libbson see its changelog: -https://github.com/mongodb/mongo-c-driver/blob/2.5.0/NEWS -http://mongoc.org/libbson/2.5.0/ +https://github.com/mongodb/mongo-c-driver/blob/2.5.3/NEWS +http://mongoc.org/libbson/2.5.3/ 0.7.0 ````` diff --git a/CMakeLists.txt b/CMakeLists.txt index 34ef921..afb0e7d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ include(FetchContent) FetchContent_Declare( mongo-c-driver - URL https://github.com/mongodb/mongo-c-driver/archive/refs/tags/2.5.0.tar.gz + URL https://github.com/mongodb/mongo-c-driver/archive/refs/tags/2.5.3.tar.gz DOWNLOAD_EXTRACT_TIMESTAMP TRUE ) diff --git a/README.rst b/README.rst index 71420c1..3df3bf9 100644 --- a/README.rst +++ b/README.rst @@ -9,7 +9,7 @@ About ===== A fast BSON to MongoDB Extended JSON converter for Python that uses -`libbson `_. +`libbson `_. Installation ============ @@ -18,8 +18,6 @@ python-bsonjs can be installed with `pip `_:: $ python -m pip install python-bsonjs -Building from source requires CMake 3.17+ (to download and build libbson). - Examples ======== @@ -59,20 +57,20 @@ Speed bsonjs is roughly 3-9x faster than PyMongo 4.18.1's json_util at decoding BSON to JSON and encoding JSON to BSON. Benchmarked -against libbson 2.5.0. See `benchmark.py`:: +against libbson 2.5.3. See `benchmark.py`:: $ python benchmark.py Timing: bsonjs.dumps(b) - 10000 loops, best of 3: 0.02494637500785757 + 10000 loops, best of 3: 0.024820834005367942 Timing: json_util.dumps(bson.decode(b)) - 10000 loops, best of 3: 0.23053841599903535 - bsonjs is 9.24x faster than json_util + 10000 loops, best of 3: 0.2280815420090221 + bsonjs is 9.19x faster than json_util Timing: bsonjs.loads(j) - 10000 loops, best of 3: 0.06266883299394976 + 10000 loops, best of 3: 0.0629402500053402 Timing: bson.encode(json_util.loads(j)) - 10000 loops, best of 3: 0.2060290410008747 - bsonjs is 3.29x faster than json_util + 10000 loops, best of 3: 0.20582279199152254 + bsonjs is 3.27x faster than json_util Limitations =========== @@ -109,7 +107,9 @@ like so Installing From Source ====================== -python-bsonjs supports CPython 3.9+. +python-bsonjs supports CPython 3.9+. Building from source downloads and +builds libbson with CMake, so you need CMake 3.17+ and an internet +connection at install time. Compiler ```````` @@ -140,3 +140,18 @@ Test To run the test suite:: $ python -m pytest + +Updating libbson +```````````````` + +The package pulls libbson from the mongo-c-driver release pinned in +`CMakeLists.txt`. To bump the version, rebuild, and refresh the benchmark +numbers in the Speed section, run:: + + $ bash bump-libbson.sh + +With no argument the script uses the latest mongo-c-driver release and +exits without making changes when the pinned version is already current. +Pass a version to target a specific release:: + + $ bash bump-libbson.sh 2.5.0 From 714a1f4366c8ee6ecae4b02b44275511071e4064 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Sat, 12 Sep 2026 19:47:14 -0500 Subject: [PATCH 31/69] Move build helpers into scripts/ and split bump-libbson Python into a module --- README.rst | 20 +-- bump-libbson.sh | 202 --------------------------- benchmark.py => scripts/benchmark.py | 0 scripts/bump-libbson.sh | 48 +++++++ scripts/bump_libbson.py | 165 ++++++++++++++++++++++ 5 files changed, 223 insertions(+), 212 deletions(-) delete mode 100755 bump-libbson.sh rename benchmark.py => scripts/benchmark.py (100%) create mode 100755 scripts/bump-libbson.sh create mode 100644 scripts/bump_libbson.py diff --git a/README.rst b/README.rst index 3df3bf9..f6ace41 100644 --- a/README.rst +++ b/README.rst @@ -57,20 +57,20 @@ Speed bsonjs is roughly 3-9x faster than PyMongo 4.18.1's json_util at decoding BSON to JSON and encoding JSON to BSON. Benchmarked -against libbson 2.5.3. See `benchmark.py`:: +against libbson 2.5.3. See `scripts/benchmark.py`:: - $ python benchmark.py + $ python scripts/benchmark.py Timing: bsonjs.dumps(b) - 10000 loops, best of 3: 0.024820834005367942 + 10000 loops, best of 3: 0.024979124995297752 Timing: json_util.dumps(bson.decode(b)) - 10000 loops, best of 3: 0.2280815420090221 - bsonjs is 9.19x faster than json_util + 10000 loops, best of 3: 0.22723987500648946 + bsonjs is 9.10x faster than json_util Timing: bsonjs.loads(j) - 10000 loops, best of 3: 0.0629402500053402 + 10000 loops, best of 3: 0.06294979200174566 Timing: bson.encode(json_util.loads(j)) - 10000 loops, best of 3: 0.20582279199152254 - bsonjs is 3.27x faster than json_util + 10000 loops, best of 3: 0.2087057090102462 + bsonjs is 3.32x faster than json_util Limitations =========== @@ -148,10 +148,10 @@ The package pulls libbson from the mongo-c-driver release pinned in `CMakeLists.txt`. To bump the version, rebuild, and refresh the benchmark numbers in the Speed section, run:: - $ bash bump-libbson.sh + $ bash scripts/bump-libbson.sh With no argument the script uses the latest mongo-c-driver release and exits without making changes when the pinned version is already current. Pass a version to target a specific release:: - $ bash bump-libbson.sh 2.5.0 + $ bash scripts/bump-libbson.sh 2.5.0 diff --git a/bump-libbson.sh b/bump-libbson.sh deleted file mode 100755 index 872866f..0000000 --- a/bump-libbson.sh +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/env bash -set -eu - -# Usage: bump-libbson.sh [LIBBSON_VERSION] -# With no argument, fetches the latest released mongo-c-driver tag. If the -# repo already pins that version, prints that it is up to date and exits -# without installing or benchmarking. - -# --- Determine the libbson version to bump to --- -CURRENT_VERSION=$(python3 - <<'PY' -import re -with open("CMakeLists.txt") as f: - text = f.read() -m = re.search(r"refs/tags/([0-9]+\.[0-9]+\.[0-9]+)\.tar\.gz", text) -if not m: - raise SystemExit("Could not read the libbson version from CMakeLists.txt") -print(m.group(1)) -PY -) - -LATEST_VERSION=$(python3 - <<'PY' -import json -import sys -import urllib.request - -url = "https://api.github.com/repos/mongodb/mongo-c-driver/releases/latest" -try: - with urllib.request.urlopen(url, timeout=30) as resp: - data = json.load(resp) -except Exception as exc: - raise SystemExit("Could not fetch the latest mongo-c-driver release: {}".format(exc)) -print(data["tag_name"].lstrip("v")) -PY -) - -if [ -z "${1:-}" ]; then - LIBBSON_VERSION="$LATEST_VERSION" - if [ "$LIBBSON_VERSION" == "$CURRENT_VERSION" ]; then - echo "libbson is already up to date (${CURRENT_VERSION})." - exit 0 - fi - echo "Found latest libbson ${LATEST_VERSION}; current is ${CURRENT_VERSION}." -else - LIBBSON_VERSION="$1" -fi - -# 1. Update the libbson version in CMakeLists.txt (the FetchContent URL), -# the README About line, and the CHANGELOG 0.8.0 entry. -python3 - "$LIBBSON_VERSION" <<'PY' -import re -import sys - -version = sys.argv[1] - - -def sub_file(path, pattern, repl, label): - with open(path) as f: - text = f.read() - if not re.search(pattern, text): - raise SystemExit( - "Could not update {} ({} not found)".format(path, label) - ) - new = re.sub(pattern, repl.format(version), text) - with open(path, "w") as f: - f.write(new) - - -sub_file( - "CMakeLists.txt", - r"refs/tags/[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz", - "refs/tags/{}.tar.gz", - "FetchContent URL", -) -# README About link: http://mongoc.org/libbson// -sub_file( - "README.rst", - r"(mongoc\.org/libbson/)[0-9]+\.[0-9]+\.[0-9]+/", - r"\g<1>{}/", - "README About link", -) -# CHANGELOG 0.8.0 entry: the libbson mention and the two URLs. Scope -# the edits to the 0.8.0 section only so historic entries are left alone. -with open("CHANGELOG.rst") as f: - changelog = f.read() - -sec = re.search(r"(?ms)^0\.8\.0\s*\n\s*```+\s*\n.*?(?=\n0\.7\.0)", changelog) -if not sec: - raise SystemExit("Could not find the CHANGELOG 0.8.0 section") -block = sec.group(0) -new_block = re.sub( - r"libbson [0-9]+\.[0-9]+\.[0-9]+ from source", - "libbson {} from source".format(version), - block, -) -new_block = re.sub( - r"mongo-c-driver/blob/[0-9]+\.[0-9]+\.[0-9]+/NEWS", - "mongo-c-driver/blob/{}/NEWS".format(version), - new_block, -) -new_block = re.sub( - r"mongoc\.org/libbson/[0-9]+\.[0-9]+\.[0-9]+/", - "mongoc.org/libbson/{}/".format(version), - new_block, -) -if not any( - re.search(p, block) - for p in ( - r"libbson [0-9]+\.[0-9]+\.[0-9]+ from source", - r"mongo-c-driver/blob/[0-9]+\.[0-9]+\.[0-9]+/NEWS", - r"mongoc\.org/libbson/[0-9]+\.[0-9]+\.[0-9]+/", - ) -): - raise SystemExit("Could not update the CHANGELOG 0.8.0 entry") -changelog = changelog[:sec.start()] + new_block + changelog[sec.end():] -with open("CHANGELOG.rst", "w") as f: - f.write(changelog) -PY - -echo "Updated libbson version to: ${LIBBSON_VERSION}" - -# 2. Install the package and the latest stable pymongo. -python3 -m pip install -e ".[test]" -python3 -m pip install --upgrade "pymongo>=4" - -# 3. Run the benchmark, capturing raw output. -BENCHMARK_OUT=$(mktemp) -python3 benchmark.py > "$BENCHMARK_OUT" 2>&1 -echo "Benchmark:" -sed 's/^/ /' "$BENCHMARK_OUT" - -# 4. Update the README Speed section with the results and versions. -PYMONGO_VERSION=$(python3 -c "import pymongo; print(pymongo.version)") -python3 - "$LIBBSON_VERSION" "$PYMONGO_VERSION" "$BENCHMARK_OUT" <<'PY' -import re -import sys - -libbson_version, pymongo_version, bench_path = sys.argv[1], sys.argv[2], sys.argv[3] -readme_path = "README.rst" - -with open(bench_path) as f: - bench = f.read() - -ratios = [ - float(x) - for x in re.findall(r"bsonjs is ([0-9.]+?)x faster", bench) -] -if len(ratios) != 2: - raise SystemExit("Expected two benchmark ratios, got: {}".format(ratios)) - -lo, hi = min(ratios), max(ratios) - -raw_numbers = re.findall(r"best of 3: ([0-9.e+-]+)", bench) -if len(raw_numbers) != 4: - raise SystemExit("Expected four benchmark timings, got: {}".format(raw_numbers)) - -dumps_bsonjs, dumps_json_util, loads_bsonjs, loads_json_util = raw_numbers - -new_block = """Speed -===== - -bsonjs is roughly {lo:.0f}-{hi:.0f}x faster than PyMongo {pymongo_version}'s -json_util at decoding BSON to JSON and encoding JSON to BSON. Benchmarked -against libbson {libbson_version}. See `benchmark.py`:: - - $ python benchmark.py - Timing: bsonjs.dumps(b) - 10000 loops, best of 3: {dumps_bsonjs} - Timing: json_util.dumps(bson.decode(b)) - 10000 loops, best of 3: {dumps_json_util} - bsonjs is {dumps_ratio:.2f}x faster than json_util - - Timing: bsonjs.loads(j) - 10000 loops, best of 3: {loads_bsonjs} - Timing: bson.encode(json_util.loads(j)) - 10000 loops, best of 3: {loads_json_util} - bsonjs is {loads_ratio:.2f}x faster than json_util -""".format( - lo=lo, - hi=hi, - pymongo_version=pymongo_version, - libbson_version=libbson_version, - dumps_bsonjs=dumps_bsonjs, - dumps_json_util=dumps_json_util, - dumps_ratio=ratios[0], - loads_bsonjs=loads_bsonjs, - loads_json_util=loads_json_util, - loads_ratio=ratios[1], -) - -with open(readme_path) as f: - readme = f.read() - -speedy = re.compile(r"Speed\n=====\n\n.*?(?=\nLimitations)", re.DOTALL) -if not speedy.search(readme): - raise SystemExit("Could not find the Speed section in README.rst") -readme = speedy.sub(new_block, readme, count=1) - -with open(readme_path, "w") as f: - f.write(readme) -PY - -echo "Updated README.rst (libbson ${LIBBSON_VERSION}, pymongo ${PYMONGO_VERSION})." diff --git a/benchmark.py b/scripts/benchmark.py similarity index 100% rename from benchmark.py rename to scripts/benchmark.py diff --git a/scripts/bump-libbson.sh b/scripts/bump-libbson.sh new file mode 100755 index 0000000..09cd518 --- /dev/null +++ b/scripts/bump-libbson.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +set -eu + +# Usage: bump-libbson.sh [LIBBSON_VERSION] +# With no argument, fetches the latest released mongo-c-driver tag. If the +# repo already pins that version, prints that it is up to date and exits +# without installing or benchmarking. + +SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SELF_DIR}/.." && pwd)" +BENCHMARK="${SELF_DIR}/benchmark.py" +BUMP_PY="${SELF_DIR}/bump_libbson.py" + +# --- Determine the libbson version to bump to --- +CURRENT_VERSION=$(python3 "${BUMP_PY}" current) +LATEST_VERSION=$(python3 "${BUMP_PY}" latest) + +if [ -z "${1:-}" ]; then + LIBBSON_VERSION="$LATEST_VERSION" + if [ "$LIBBSON_VERSION" == "$CURRENT_VERSION" ]; then + echo "libbson is already up to date (${CURRENT_VERSION})." + exit 0 + fi + echo "Found latest libbson ${LATEST_VERSION}; current is ${CURRENT_VERSION}." +else + LIBBSON_VERSION="$1" +fi + +# 1. Update the libbson version in CMakeLists.txt, the README About line, +# and the CHANGELOG 0.8.0 entry. +python3 "${BUMP_PY}" update-versions "$LIBBSON_VERSION" +echo "Updated libbson version to: ${LIBBSON_VERSION}" + +# 2. Install the package and the latest stable pymongo. +cd "${REPO_ROOT}" +python3 -m pip install -e ".[test]" +python3 -m pip install --upgrade "pymongo>=4" + +# 3. Run the benchmark, capturing raw output. +BENCHMARK_OUT=$(mktemp) +python3 "${BENCHMARK}" > "$BENCHMARK_OUT" 2>&1 +echo "Benchmark:" +sed 's/^/ /' "$BENCHMARK_OUT" + +# 4. Update the README Speed section with the results and versions. +PYMONGO_VERSION=$(python3 -c "import pymongo; print(pymongo.version)") +python3 "${BUMP_PY}" update-readme "$LIBBSON_VERSION" "$PYMONGO_VERSION" "$BENCHMARK_OUT" +echo "Updated README.rst (libbson ${LIBBSON_VERSION}, pymongo ${PYMONGO_VERSION})." diff --git a/scripts/bump_libbson.py b/scripts/bump_libbson.py new file mode 100644 index 0000000..6f9d52e --- /dev/null +++ b/scripts/bump_libbson.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +"""Version bump helper used by bump-libbson.sh.""" + +import argparse +import json +import re +import urllib.request +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +README = REPO_ROOT / "README.rst" +CHANGELOG = REPO_ROOT / "CHANGELOG.rst" +CMAKE_LISTS = REPO_ROOT / "CMakeLists.txt" + +TAG_URL = r"refs/tags/[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz" +LATEST_RELEASE_URL = ( + "https://api.github.com/repos/mongodb/mongo-c-driver/releases/latest" +) + + +def current_version(): + """Return the libbson version pinned in CMakeLists.txt.""" + text = CMAKE_LISTS.read_text() + m = re.search(TAG_URL, text) + if not m: + raise SystemExit("Could not read the libbson version from CMakeLists.txt") + return re.search(r"([0-9]+\.[0-9]+\.[0-9]+)", m.group(0)).group(1) + + +def latest_version(): + """Return the latest released mongo-c-driver tag.""" + try: + with urllib.request.urlopen(LATEST_RELEASE_URL, timeout=30) as resp: + data = json.load(resp) + except Exception as exc: + raise SystemExit( + "Could not fetch the latest mongo-c-driver release: {}".format(exc) + ) + return data["tag_name"].lstrip("v") + + +def sub_file(path, pattern, repl, label): + """Replace the first match of pattern in path with repl.format(version).""" + text = path.read_text() + if not re.search(pattern, text): + raise SystemExit("Could not update {} ({} not found)".format(path, label)) + path.write_text(re.sub(pattern, repl, text)) + + +def update_versions(version): + """Update the pinned libbson version in the build files and docs.""" + sub_file( + CMAKE_LISTS, + TAG_URL, + "refs/tags/{}.tar.gz".format(version), + "FetchContent URL", + ) + # README About link: http://mongoc.org/libbson// + sub_file( + README, + r"(mongoc\.org/libbson/)[0-9]+\.[0-9]+\.[0-9]+/", + r"\g<1>{}/".format(version), + "README About link", + ) + # CHANGELOG 0.8.0 entry. Scope the edits to that section only so historic + # entries are left alone. + changelog = CHANGELOG.read_text() + sec = re.search(r"(?ms)^0\.8\.0\s*\n\s*```+\s*\n.*?(?=\n0\.7\.0)", changelog) + if not sec: + raise SystemExit("Could not find the CHANGELOG 0.8.0 section") + block = sec.group(0) + patterns = ( + (r"libbson [0-9]+\.[0-9]+\.[0-9]+ from source", + "libbson {} from source".format(version)), + (r"mongo-c-driver/blob/[0-9]+\.[0-9]+\.[0-9]+/NEWS", + "mongo-c-driver/blob/{}/NEWS".format(version)), + (r"mongoc\.org/libbson/[0-9]+\.[0-9]+\.[0-9]+/", + "mongoc.org/libbson/{}/".format(version)), + ) + if not any(re.search(p, block) for p, _ in patterns): + raise SystemExit("Could not update the CHANGELOG 0.8.0 entry") + for pattern, repl in patterns: + block = re.sub(pattern, repl, block) + CHANGELOG.write_text(changelog[:sec.start()] + block + changelog[sec.end():]) + + +def update_readme(version, pymongo_version, bench_path): + """Rewrite the README Speed section using the measured benchmark output.""" + bench = Path(bench_path).read_text() + + ratios = [float(x) for x in re.findall(r"bsonjs is ([0-9.]+?)x faster", bench)] + if len(ratios) != 2: + raise SystemExit("Expected two benchmark ratios, got: {}".format(ratios)) + lo, hi = min(ratios), max(ratios) + + raw_numbers = re.findall(r"best of 3: ([0-9.e+-]+)", bench) + if len(raw_numbers) != 4: + raise SystemExit( + "Expected four benchmark timings, got: {}".format(raw_numbers) + ) + dumps_bsonjs, dumps_json_util, loads_bsonjs, loads_json_util = raw_numbers + + new_block = """Speed +===== + +bsonjs is roughly {lo:.0f}-{hi:.0f}x faster than PyMongo {pymongo_version}'s +json_util at decoding BSON to JSON and encoding JSON to BSON. Benchmarked +against libbson {libbson_version}. See `scripts/benchmark.py`:: + + $ python scripts/benchmark.py + Timing: bsonjs.dumps(b) + 10000 loops, best of 3: {dumps_bsonjs} + Timing: json_util.dumps(bson.decode(b)) + 10000 loops, best of 3: {dumps_json_util} + bsonjs is {dumps_ratio:.2f}x faster than json_util + + Timing: bsonjs.loads(j) + 10000 loops, best of 3: {loads_bsonjs} + Timing: bson.encode(json_util.loads(j)) + 10000 loops, best of 3: {loads_json_util} + bsonjs is {loads_ratio:.2f}x faster than json_util +""".format( + lo=lo, + hi=hi, + pymongo_version=pymongo_version, + libbson_version=version, + dumps_bsonjs=dumps_bsonjs, + dumps_json_util=dumps_json_util, + dumps_ratio=ratios[0], + loads_bsonjs=loads_bsonjs, + loads_json_util=loads_json_util, + loads_ratio=ratios[1], + ) + + readme = README.read_text() + speedy = re.compile(r"Speed\n=====\n\n.*?(?=\nLimitations)", re.DOTALL) + if not speedy.search(readme): + raise SystemExit("Could not find the Speed section in README.rst") + README.write_text(speedy.sub(new_block, readme, count=1)) + + +def main(): + parser = argparse.ArgumentParser() + sub = parser.add_subparsers(dest="cmd", required=True) + sub.add_parser("current") + sub.add_parser("latest") + sub.add_parser("update-versions").add_argument("version") + readme = sub.add_parser("update-readme") + readme.add_argument("version") + readme.add_argument("pymongo_version") + readme.add_argument("bench_path") + args = parser.parse_args() + + if args.cmd == "current": + print(current_version()) + elif args.cmd == "latest": + print(latest_version()) + elif args.cmd == "update-versions": + update_versions(args.version) + elif args.cmd == "update-readme": + update_readme(args.version, args.pymongo_version, args.bench_path) + + +if __name__ == "__main__": + main() From c5a0b408597993a61afe47d2adb1785af3b4778f Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 06:22:52 -0500 Subject: [PATCH 32/69] Disable libmongoc in the CMake build and fix bump-libbson nits The cibuildwheel build failed because FetchContent configured the whole mongo-c-driver, and libmongoc requires a TLS/crypto library the manylinux container does not provide. Turn off mongoc (and build libbson statically, as a shared lib is never used) so only libbson is built. Also address review nits: update the stale MSVC 2015 README reference, derive the CHANGELOG section from the package version instead of hardcoding 0.8.0, and clean up the benchmark temp file. --- CMakeLists.txt | 6 ++++++ README.rst | 2 +- scripts/bump-libbson.sh | 1 + scripts/bump_libbson.py | 27 ++++++++++++++++++++++----- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index afb0e7d..cb849d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,12 @@ FetchContent_Declare( DOWNLOAD_EXTRACT_TIMESTAMP TRUE ) +# We only need libbson, not libmongoc, which requires a TLS/crypto library. +# Turning mongoc off also keeps the build C-only and the driver's dependencies +# out of the picture. Build libbson as a static library. +set(ENABLE_MONGOC OFF) +set(ENABLE_SHARED OFF) +set(ENABLE_STATIC ON) # The driver builds a C++ test gated on BUILD_TESTING; keep the build C-only. set(BUILD_TESTING OFF) set(ENABLE_TESTS OFF) diff --git a/README.rst b/README.rst index f6ace41..cecbf27 100644 --- a/README.rst +++ b/README.rst @@ -118,7 +118,7 @@ You must build python-bsonjs separately for each version of Python. On Windows this means you must use the same C compiler your Python version was built with. -- Windows build requires Microsoft Visual Studio 2015 +- Windows build requires Microsoft Visual Studio 2019 or newer Source `````` diff --git a/scripts/bump-libbson.sh b/scripts/bump-libbson.sh index 09cd518..4cfcd1b 100755 --- a/scripts/bump-libbson.sh +++ b/scripts/bump-libbson.sh @@ -38,6 +38,7 @@ python3 -m pip install --upgrade "pymongo>=4" # 3. Run the benchmark, capturing raw output. BENCHMARK_OUT=$(mktemp) +trap 'rm -f "$BENCHMARK_OUT"' EXIT python3 "${BENCHMARK}" > "$BENCHMARK_OUT" 2>&1 echo "Benchmark:" sed 's/^/ /' "$BENCHMARK_OUT" diff --git a/scripts/bump_libbson.py b/scripts/bump_libbson.py index 6f9d52e..ad7fe5f 100644 --- a/scripts/bump_libbson.py +++ b/scripts/bump_libbson.py @@ -18,6 +18,16 @@ ) +def package_version(): + """Return the package release version (X.Y.Z) from pyproject.toml.""" + text = (REPO_ROOT / "pyproject.toml").read_text() + m = re.search(r'version\s*=\s*"([^"]+)"', text) + if not m: + raise SystemExit("Could not read the version from pyproject.toml") + # Strip any dev/alpha/beta/rc suffix so we match the CHANGELOG header. + return re.match(r"(\d+\.\d+(?:\.\d+)?)", m.group(1)).group(1) + + def current_version(): """Return the libbson version pinned in CMakeLists.txt.""" text = CMAKE_LISTS.read_text() @@ -62,12 +72,19 @@ def update_versions(version): r"\g<1>{}/".format(version), "README About link", ) - # CHANGELOG 0.8.0 entry. Scope the edits to that section only so historic - # entries are left alone. + # CHANGELOG section for the current package release. Scope the edits to + # that section only so historic entries are left alone; the lower bound is + # the next section header (any version), so future bumps stay correct. changelog = CHANGELOG.read_text() - sec = re.search(r"(?ms)^0\.8\.0\s*\n\s*```+\s*\n.*?(?=\n0\.7\.0)", changelog) + ver = re.escape(package_version()) + sec = re.search( + r"(?ms)^{ver}\s*\n\s*```+\s*\n.*?(?=^\d+\.\d+\.\d+\s*\n\s*```+)".format( + ver=ver + ), + changelog, + ) if not sec: - raise SystemExit("Could not find the CHANGELOG 0.8.0 section") + raise SystemExit("Could not find the CHANGELOG {} section".format(ver)) block = sec.group(0) patterns = ( (r"libbson [0-9]+\.[0-9]+\.[0-9]+ from source", @@ -78,7 +95,7 @@ def update_versions(version): "mongoc.org/libbson/{}/".format(version)), ) if not any(re.search(p, block) for p, _ in patterns): - raise SystemExit("Could not update the CHANGELOG 0.8.0 entry") + raise SystemExit("Could not update the CHANGELOG {} entry".format(ver)) for pattern, repl in patterns: block = re.sub(pattern, repl, block) CHANGELOG.write_text(changelog[:sec.start()] + block + changelog[sec.end():]) From 10d786b84ff51fba9bb8c4248d96cd584a33b3ff Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 06:44:02 -0500 Subject: [PATCH 33/69] Bump minimum supported Python to 3.11 Raise the floor to CPython 3.11 so the extension can target a more feature-complete Limited API (0x030b0000) and the wheel tags as cp311-abi3. Drops the 3.9 and 3.10 build matrix entries and updates the classifiers, README, CHANGELOG, and the sdist builder version. --- .github/workflows/codeql.yml | 2 +- .github/workflows/dist.yml | 2 +- .github/workflows/test.yml | 2 +- CHANGELOG.rst | 4 +++- CMakeLists.txt | 2 +- README.rst | 2 +- pyproject.toml | 6 ++---- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2435694..ab09bc7 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -47,7 +47,7 @@ jobs: -Ibsonjs -I"$SRC/src/libbson/src" -I"$SRC/src/libbson/src/bson" \ -I"$BIN/src/libbson/src" -I"$SRC/src/common/src" -I"$BIN/src/common/src" \ -I"$PYINC" \ - -DBSON_STATIC -DPy_LIMITED_API=0x03090000 "-DBSONJS_VERSION=\"$BSONJS_VERSION\"" + -DBSON_STATIC -DPy_LIMITED_API=0x030b0000 "-DBSONJS_VERSION=\"$BSONJS_VERSION\"" - language: python build-mode: none - language: actions diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 83da385..6161e55 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -50,7 +50,7 @@ jobs: - name: Setup Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: - python-version: 3.9 + python-version: 3.11 - name: Build SDist run: | python -m pip install build diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 16440cb..ef2fc7c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6e827e2..6a0d63c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,7 +4,9 @@ Changelog 0.8.0 ````` Version 0.8.0 builds libbson 2.5.3 from source at install time instead of -vendoring C sources. Building from source requires CMake 3.17+. +vendoring C sources. Building from source requires CMake 3.17+ and Python +3.11+ (up from 3.9), so the extension is built against the CPython 3.11 +Limited API. For a detailed breakdown of what changed in each version of libbson see its changelog: https://github.com/mongodb/mongo-c-driver/blob/2.5.3/NEWS http://mongoc.org/libbson/2.5.3/ diff --git a/CMakeLists.txt b/CMakeLists.txt index cb849d1..e77f1b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,7 @@ target_include_directories(bsonjs PRIVATE ) target_compile_definitions(bsonjs PRIVATE - Py_LIMITED_API=0x03090000 + Py_LIMITED_API=0x030b0000 BSONJS_VERSION="${_package_version}" BSON_STATIC ) diff --git a/README.rst b/README.rst index cecbf27..f8fc637 100644 --- a/README.rst +++ b/README.rst @@ -107,7 +107,7 @@ like so Installing From Source ====================== -python-bsonjs supports CPython 3.9+. Building from source downloads and +python-bsonjs supports CPython 3.11+. Building from source downloads and builds libbson with CMake, so you need CMake 3.17+ and an internet connection at install time. diff --git a/pyproject.toml b/pyproject.toml index ed2ce34..8e8eaa5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ version = "0.8.0.dev0" description = "A library for converting between BSON and JSON." readme = "README.rst" license = { file = "LICENSE" } -requires-python = ">=3.9" +requires-python = ">=3.11" authors = [ { name = "Shane Harvey", email = "shane.harvey@mongodb.com" }, ] @@ -26,9 +26,7 @@ classifiers = [ "Operating System :: POSIX", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", @@ -43,7 +41,7 @@ test = ["pymongo>=4", "pytest"] [tool.scikit-build] wheel.packages = [] -wheel.py-api = "cp39" +wheel.py-api = "cp311" [tool.cibuildwheel] test-command = "pytest {package}/test" From 65255443cd6dd879f665c27f5110918c2a028f2c Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 09:32:50 -0500 Subject: [PATCH 34/69] Build the extension as a Stable ABI module so abi3 wheels work everywhere The CPython-311 Limited API wheel was failing its test across every platform: the extension was built with WITH_SOABI, so it was named after the build interpreter (bsonjs.cpython-311-*.so). Newer interpreters (cp312+) don't list that suffix, so `import bsonjs` resolved to the source `bsonjs/` namespace instead of the installed module, and the tests failed with `AttributeError: module 'bsonjs' has no attribute 'LEGACY'`. Build with `Python_add_library(... USE_SABI 3.11 ...)` instead, which links Python::SABIModule and derives Py_LIMITED_API, so the single cp311-abi3 wheel is discovered and imported on every CPython >= 3.11. This needs CMake 3.26+, so bump the requirement. Also skip the free- threaded (cp314t/cp315t) builds, which are not supported. --- CHANGELOG.rst | 2 +- CMakeLists.txt | 12 ++++++++---- README.rst | 2 +- pyproject.toml | 4 ++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6a0d63c..2ec24dd 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,7 +4,7 @@ Changelog 0.8.0 ````` Version 0.8.0 builds libbson 2.5.3 from source at install time instead of -vendoring C sources. Building from source requires CMake 3.17+ and Python +vendoring C sources. Building from source requires CMake 3.26+ and Python 3.11+ (up from 3.9), so the extension is built against the CPython 3.11 Limited API. For a detailed breakdown of what changed in each version of libbson see its changelog: diff --git a/CMakeLists.txt b/CMakeLists.txt index e77f1b2..cf927db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ -cmake_minimum_required(VERSION 3.17) +cmake_minimum_required(VERSION 3.26) project(bsonjs LANGUAGES C) -find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) +find_package(Python COMPONENTS Interpreter Development.Module Development.SABIModule REQUIRED) # Read the package version from pyproject.toml for BSONJS_VERSION. file(READ "${CMAKE_CURRENT_SOURCE_DIR}/pyproject.toml" _pyproject) @@ -39,7 +39,12 @@ set(_common_src "${mongo-c-driver_SOURCE_DIR}/src/common") set(_common_bin "${mongo-c-driver_BINARY_DIR}/src/common") # --- Build the extension module --- -Python_add_library(bsonjs MODULE WITH_SOABI "${CMAKE_CURRENT_SOURCE_DIR}/bsonjs/bsonjs.c") +# USE_SABI links Python::SABIModule and sets Py_LIMITED_API from the version, +# producing a module suffix that stays importable across every CPython >= 3.11, +# so a single cp311-abi3 wheel is reused for all of them. (WITH_SOABI would bake +# the build interpreter's SOABI into the name, which newer interpreters can't +# discover.) +Python_add_library(bsonjs MODULE USE_SABI 3.11 "${CMAKE_CURRENT_SOURCE_DIR}/bsonjs/bsonjs.c") target_include_directories(bsonjs PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/bsonjs" @@ -51,7 +56,6 @@ target_include_directories(bsonjs PRIVATE ) target_compile_definitions(bsonjs PRIVATE - Py_LIMITED_API=0x030b0000 BSONJS_VERSION="${_package_version}" BSON_STATIC ) diff --git a/README.rst b/README.rst index f8fc637..ea9c1f9 100644 --- a/README.rst +++ b/README.rst @@ -108,7 +108,7 @@ Installing From Source ====================== python-bsonjs supports CPython 3.11+. Building from source downloads and -builds libbson with CMake, so you need CMake 3.17+ and an internet +builds libbson with CMake, so you need CMake 3.26+ and an internet connection at install time. Compiler diff --git a/pyproject.toml b/pyproject.toml index 8e8eaa5..65a09d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["scikit-build-core>=0.10", "cmake>=3.17", "ninja"] +requires = ["scikit-build-core>=0.10", "cmake>=3.26", "ninja"] build-backend = "scikit_build_core.build" [project] @@ -46,7 +46,7 @@ wheel.py-api = "cp311" [tool.cibuildwheel] test-command = "pytest {package}/test" test-extras = ["test"] -skip = ["cp314t-*"] +skip = ["cp314t-*", "cp315t-*"] # Use abi3audit to catch issues with Limited API wheels [tool.cibuildwheel.linux] From 84b95aaed3d830603f5857a1892bc9956efbfb66 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 09:40:18 -0500 Subject: [PATCH 35/69] Fetch mongo-c-driver via a shallow git clone for resilient downloads The FetchContent URL download of the 2.5.3 tarball from GitHub codeload was failing across the build matrix with transient HTTP 504s, taking down every build. A shallow git clone uses the git-over-HTTPS path, which is independent of codeload and tolerates transient failures, so the configure step stops being a single point of flakiness. --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cf927db..764fa1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,8 +13,9 @@ include(FetchContent) FetchContent_Declare( mongo-c-driver - URL https://github.com/mongodb/mongo-c-driver/archive/refs/tags/2.5.3.tar.gz - DOWNLOAD_EXTRACT_TIMESTAMP TRUE + GIT_REPOSITORY https://github.com/mongodb/mongo-c-driver.git + GIT_TAG 2.5.3 + GIT_SHALLOW TRUE ) # We only need libbson, not libmongoc, which requires a TLS/crypto library. From 6d4986f76ede5e112baa25132786bb9c080f8972 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 10:17:28 -0500 Subject: [PATCH 36/69] Add ccache to CI to speed up the repeated libbson build Every build compiles the fetched libbson from source, which is the bulk of the work. Wrap the compiler in ccache so that work is cached across jobs and runs. A composite setup-ccache action installs ccache, configures it, and restores/saves the cache via actions/cache; CMakeLists detects ccache and sets CMAKE_C_COMPILER_LAUNCHER before project() so the launcher also covers the FetchContent sub-build. The cibuildwheel Linux container installs ccache best-effort (never fails the build). --- .github/actions/setup-ccache/action.yml | 59 +++++++++++++++++++++++++ .github/workflows/dist.yml | 5 +++ .github/workflows/test.yml | 5 +++ CMakeLists.txt | 9 ++++ pyproject.toml | 3 ++ 5 files changed, 81 insertions(+) create mode 100644 .github/actions/setup-ccache/action.yml diff --git a/.github/actions/setup-ccache/action.yml b/.github/actions/setup-ccache/action.yml new file mode 100644 index 0000000..1cddb3b --- /dev/null +++ b/.github/actions/setup-ccache/action.yml @@ -0,0 +1,59 @@ +name: Setup ccache +description: "Install ccache, configure it, and restore/save the cache" +inputs: + key: + description: "Cache key for actions/cache" + required: true + restore-keys: + description: "Restore-key prefix for actions/cache" + required: true + maxsize: + description: "CCACHE_MAXSIZE" + required: false + default: "400M" + sloppiness: + description: "CCACHE_SLOPPINESS" + required: false + default: "pch_defines,time_macros" +runs: + using: "composite" + steps: + - name: Configure ccache + shell: bash + env: + CCACHE_MAXSIZE: ${{ inputs.maxsize }} + CCACHE_SLOPPINESS: ${{ inputs.sloppiness }} + run: | + { + echo "CCACHE_BASEDIR=$GITHUB_WORKSPACE" + echo "CCACHE_DIR=$GITHUB_WORKSPACE/.ccache" + echo "CCACHE_COMPRESS=true" + echo "CCACHE_COMPRESSLEVEL=6" + echo "CCACHE_MAXSIZE=$CCACHE_MAXSIZE" + echo "CCACHE_SLOPPINESS=$CCACHE_SLOPPINESS" + } >> "$GITHUB_ENV" + + - name: Install ccache + shell: bash + run: | + if [ "$RUNNER_OS" = "Linux" ]; then + sudo apt-get install -y ccache + sudo /usr/sbin/update-ccache-symlinks + echo "/usr/lib/ccache" >> "$GITHUB_PATH" + elif [ "$RUNNER_OS" = "macOS" ]; then + brew install ccache + echo "$(brew --prefix ccache)/libexec" >> "$GITHUB_PATH" + elif [ "$RUNNER_OS" = "Windows" ]; then + choco install ccache --no-progress -y + fi + + - name: Restore/save ccache + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/.ccache + key: ${{ inputs.key }} + restore-keys: ${{ inputs.restore-keys }} + + - name: Zero ccache stats + shell: bash + run: ccache --zero-stats diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 6161e55..d129be0 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -34,6 +34,11 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false + - name: Set up ccache + uses: ./.github/actions/setup-ccache + with: + key: ccache-${{ runner.os }}-${{ github.sha }} + restore-keys: ccache-${{ runner.os }}- - uses: pypa/cibuildwheel@4726cd35bb13f7bde50cf2761f2499ac7b3aa32c # v4.1.1 - uses: actions/upload-artifact@v7 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ef2fc7c..e79e986 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,11 @@ jobs: with: python-version: ${{ matrix.python-version }} allow-prereleases: true + - name: Set up ccache + uses: ./.github/actions/setup-ccache + with: + key: ccache-${{ runner.os }}-py${{ matrix.python-version }}-${{ github.sha }} + restore-keys: ccache-${{ runner.os }}-py${{ matrix.python-version }}- - name: Test with python run: | python -m pip install -v -e ".[test]" diff --git a/CMakeLists.txt b/CMakeLists.txt index 764fa1d..3f2ba46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,13 @@ cmake_minimum_required(VERSION 3.26) + +# Wrap the compiler in ccache when it's available so the repeated libbson +# build in CI is served from the cache. Must be set before project() so the +# launcher applies to the C compiler (and the FetchContent sub-build). +find_program(CCACHE_PROGRAM ccache) +if(CCACHE_PROGRAM) + set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") +endif() + project(bsonjs LANGUAGES C) find_package(Python COMPONENTS Interpreter Development.Module Development.SABIModule REQUIRED) diff --git a/pyproject.toml b/pyproject.toml index 65a09d9..5d57f6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,9 @@ skip = ["cp314t-*", "cp315t-*"] # Use abi3audit to catch issues with Limited API wheels [tool.cibuildwheel.linux] +# Best-effort: make ccache available in the manylinux container so the +# compiler launcher picks it up. CCACHE_DIR is passed through by cibuildwheel. +before-all = "yum install -y ccache || true" repair-wheel-command = [ "auditwheel repair -w {dest_dir} {wheel}", "pipx run abi3audit --strict --report {wheel}", From f67f86bfe99ebd3c47d7e8cc1fe909959f48ecc7 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 11:20:42 -0500 Subject: [PATCH 37/69] Build the wheel once per platform and test it against every Python version The source-build test matrix compiled libbson from source once per Python version on each OS (4x on Windows), which was the slow/hung step. Since the project builds a single cp311-abi3 wheel that works on CPython >= 3.11, build it once per platform and have a dependent test job install that wheel and run pytest across 3.11-3.14. This removes the repeated libbson compiles while keeping full-version test coverage. --- .github/workflows/test.yml | 47 +++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e79e986..9e458eb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,30 +10,61 @@ concurrency: group: test-${{ github.ref }} cancel-in-progress: true +defaults: + run: + shell: bash + jobs: + # Build the cp311-abi3 wheel once per platform. The test job reuses it across + # every supported Python version, so libbson is only compiled once per OS + # instead of once per Python version. build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-latest, ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.11" + - name: Set up ccache + uses: ./.github/actions/setup-ccache + with: + key: ccache-${{ runner.os }}-wheel-${{ github.sha }} + restore-keys: ccache-${{ runner.os }}-wheel- + - name: Build wheel + run: python -m pip wheel --no-deps -w dist . + - uses: actions/upload-artifact@v7 + with: + name: wheel-${{ matrix.os }} + path: ./dist/*.whl + test: + needs: build runs-on: ${{ matrix.os }} strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] python-version: ["3.11", "3.12", "3.13", "3.14"] - steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false + - name: Download wheel + uses: actions/download-artifact@v8 + with: + name: wheel-${{ matrix.os }} + path: dist - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - - name: Set up ccache - uses: ./.github/actions/setup-ccache - with: - key: ccache-${{ runner.os }}-py${{ matrix.python-version }}-${{ github.sha }} - restore-keys: ccache-${{ runner.os }}-py${{ matrix.python-version }}- - - name: Test with python + - name: Test against wheel run: | - python -m pip install -v -e ".[test]" + python -m pip install "pymongo>=4" pytest dist/*.whl pytest From 8a7e2986108a984efce1d92dd584d2e6a16c673e Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 11:32:26 -0500 Subject: [PATCH 38/69] Fix zizmor findings in workflow changes - setup-ccache: stop writing to GITHUB_PATH; the compiler launcher in CMakeLists already routes the compiler through ccache, so the PATH hack was unnecessary and zizmor flagged it as a code-injection risk. - test.yml: scope permissions to contents: read. - codeql.yml: the # v3 pin pointed to a stale commit, not the v3 tag; pin the actual v3 HEAD. --- .github/actions/setup-ccache/action.yml | 3 --- .github/workflows/codeql.yml | 2 +- .github/workflows/test.yml | 3 +++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/setup-ccache/action.yml b/.github/actions/setup-ccache/action.yml index 1cddb3b..e6dfc83 100644 --- a/.github/actions/setup-ccache/action.yml +++ b/.github/actions/setup-ccache/action.yml @@ -38,11 +38,8 @@ runs: run: | if [ "$RUNNER_OS" = "Linux" ]; then sudo apt-get install -y ccache - sudo /usr/sbin/update-ccache-symlinks - echo "/usr/lib/ccache" >> "$GITHUB_PATH" elif [ "$RUNNER_OS" = "macOS" ]; then brew install ccache - echo "$(brew --prefix ccache)/libexec" >> "$GITHUB_PATH" elif [ "$RUNNER_OS" = "Windows" ]; then choco install ccache --no-progress -y fi diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ab09bc7..f3eacac 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -53,7 +53,7 @@ jobs: - language: actions build-mode: none steps: - - uses: mongodb-labs/drivers-github-tools/codeql@d518d2c7d04fdec10266c4218c36791a4fcf98d8 # v3 + - uses: mongodb-labs/drivers-github-tools/codeql@f137fdd28483af14ebf466ebc5aa789fbf867218 # v3 with: language: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9e458eb..bc8ee7f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,9 @@ on: pull_request: workflow_dispatch: +permissions: + contents: read + concurrency: group: test-${{ github.ref }} cancel-in-progress: true From 047954820fa31617e3871572cf9f19f37b3c78e6 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 12:17:12 -0500 Subject: [PATCH 39/69] Split the build into per-platform jobs so tests don't wait on each other A single matrix build job gated every platform's tests on all builds, so macOS/Ubuntu tests were held up behind the slow Windows compile. Make build and test per-OS jobs: each OS builds its abi3 wheel once, uploads it, and its test jobs (matrix over Python 3.11-3.14) install that wheel and run pytest. macOS/Ubuntu now finish without waiting for Windows. --- .github/workflows/test.yml | 122 ++++++++++++++++++++++++++++++++----- 1 file changed, 108 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bc8ee7f..83466f0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,14 +18,32 @@ defaults: shell: bash jobs: - # Build the cp311-abi3 wheel once per platform. The test job reuses it across - # every supported Python version, so libbson is only compiled once per OS - # instead of once per Python version. - build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [macos-latest, ubuntu-latest, windows-latest] + # Build the cp311-abi3 wheel once per platform, as separate jobs so each + # OS's tests only wait on its own build (not on the others). + build-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.11" + - name: Set up ccache + uses: ./.github/actions/setup-ccache + with: + key: ccache-${{ runner.os }}-wheel-${{ github.sha }} + restore-keys: ccache-${{ runner.os }}-wheel- + - name: Build wheel + run: python -m pip wheel --no-deps -w dist . + - uses: actions/upload-artifact@v7 + with: + name: wheel-macos + path: ./dist/*.whl + + build-ubuntu: + runs-on: ubuntu-latest steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -43,15 +61,91 @@ jobs: run: python -m pip wheel --no-deps -w dist . - uses: actions/upload-artifact@v7 with: - name: wheel-${{ matrix.os }} + name: wheel-ubuntu path: ./dist/*.whl - test: - needs: build - runs-on: ${{ matrix.os }} + build-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.11" + - name: Set up ccache + uses: ./.github/actions/setup-ccache + with: + key: ccache-${{ runner.os }}-wheel-${{ github.sha }} + restore-keys: ccache-${{ runner.os }}-wheel- + - name: Build wheel + run: python -m pip wheel --no-deps -w dist . + - uses: actions/upload-artifact@v7 + with: + name: wheel-windows + path: ./dist/*.whl + + # Install the prebuilt wheel and run the tests on every supported Python + # version. Each platform's tests depend only on that platform's build. + test-macos: + needs: build-macos + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.11", "3.12", "3.13", "3.14"] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Download wheel + uses: actions/download-artifact@v8 + with: + name: wheel-macos + path: dist + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: ${{ matrix.python-version }} + allow-prereleases: true + - name: Test against wheel + run: | + python -m pip install "pymongo>=4" pytest dist/*.whl + pytest + + test-ubuntu: + needs: build-ubuntu + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.11", "3.12", "3.13", "3.14"] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Download wheel + uses: actions/download-artifact@v8 + with: + name: wheel-ubuntu + path: dist + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: ${{ matrix.python-version }} + allow-prereleases: true + - name: Test against wheel + run: | + python -m pip install "pymongo>=4" pytest dist/*.whl + pytest + + test-windows: + needs: build-windows + runs-on: windows-latest strategy: + fail-fast: false matrix: - os: [macos-latest, ubuntu-latest, windows-latest] python-version: ["3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -60,7 +154,7 @@ jobs: - name: Download wheel uses: actions/download-artifact@v8 with: - name: wheel-${{ matrix.os }} + name: wheel-windows path: dist - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 From 5432797025efc5f7dc07a943081d22e235f9a4d4 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 12:21:43 -0500 Subject: [PATCH 40/69] Report ccache statistics after each wheel build The build logs gave no evidence that ccache was actually used, since CMake's compiler launcher is silent. Print `ccache -s` after the build so the stats show hits/misses and confirm the cache is being populated and reused. --- .github/workflows/test.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 83466f0..b0655cc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,6 +37,8 @@ jobs: restore-keys: ccache-${{ runner.os }}-wheel- - name: Build wheel run: python -m pip wheel --no-deps -w dist . + - name: Show ccache stats + run: ccache -s - uses: actions/upload-artifact@v7 with: name: wheel-macos @@ -59,6 +61,8 @@ jobs: restore-keys: ccache-${{ runner.os }}-wheel- - name: Build wheel run: python -m pip wheel --no-deps -w dist . + - name: Show ccache stats + run: ccache -s - uses: actions/upload-artifact@v7 with: name: wheel-ubuntu @@ -81,6 +85,8 @@ jobs: restore-keys: ccache-${{ runner.os }}-wheel- - name: Build wheel run: python -m pip wheel --no-deps -w dist . + - name: Show ccache stats + run: ccache -s - uses: actions/upload-artifact@v7 with: name: wheel-windows From 9ecd0efddc811b291080da06f185f51764c8a292 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 12:33:21 -0500 Subject: [PATCH 41/69] Build in a deterministic directory so ccache hits across CI runs The ccache stats showed 30/30 cacheable calls but 0% hits: pip wheel's build isolation uses a fresh temp dir each run, so the fetched mongo-c-driver paths change and the ccache keys never match. Set a stable build-dir so libbson's compile is cached and reused on subsequent runs, which is where the real CI time goes. --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 5d57f6c..ab44e7b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,10 @@ test = ["pymongo>=4", "pytest"] [tool.scikit-build] wheel.packages = [] wheel.py-api = "cp311" +# Deterministic build dir: pip wheel's default build isolation uses a fresh +# temp dir each run, which changes the fetched mongo-c-driver paths and defeats +# ccache (no cross-run hits). Building here keeps the paths stable. +build-dir = "build" [tool.cibuildwheel] test-command = "pytest {package}/test" From 53d799c6889892e61f1ddc9e3f418e951b2cf36e Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 15:06:08 -0500 Subject: [PATCH 42/69] Fetch mongo-c-driver from a retryable tarball instead of a git clone The git clone hangs on the Windows CI runners: the build emitted no output for hours at the FetchContent clone step, while the same clone takes ~2s on macOS. Download the release tarball once with retries and feed it to FetchContent. This avoids git on every platform and also tolerates the transient 504s the bare tarball URL hit earlier. --- CMakeLists.txt | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f2ba46..edae5e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,11 +20,29 @@ set(_package_version "${CMAKE_MATCH_1}") # --- libbson static library via FetchContent --- include(FetchContent) +# A git clone of mongo-c-driver hangs on the Windows CI runners (no output for +# hours), and a bare tarball URL can hit transient HTTP 504s. Download the +# release tarball once with retries and feed the local archive to FetchContent, +# which is deterministic on every platform. +set(_mcd_url "https://github.com/mongodb/mongo-c-driver/archive/refs/tags/2.5.3.tar.gz") +set(_mcd_archive "${CMAKE_CURRENT_BINARY_DIR}/mongo-c-driver-2.5.3.tar.gz") +file(DOWNLOAD + "${_mcd_url}" + "${_mcd_archive}" + TLS_VERIFY ON + RETRY_COUNT 3 + RETRY_DELAY 5 + STATUS _mcd_status +) +list(GET _mcd_status 0 _mcd_code) +if(NOT _mcd_code EQUAL 0) + message(FATAL_ERROR "Failed to download mongo-c-driver: ${_mcd_status}") +endif() + FetchContent_Declare( mongo-c-driver - GIT_REPOSITORY https://github.com/mongodb/mongo-c-driver.git - GIT_TAG 2.5.3 - GIT_SHALLOW TRUE + URL "${_mcd_archive}" + DOWNLOAD_EXTRACT_TIMESTAMP TRUE ) # We only need libbson, not libmongoc, which requires a TLS/crypto library. From 281c020f900bf001ec0afe1395e36a8a9500c305 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 16:00:10 -0500 Subject: [PATCH 43/69] Only use ccache as the compiler launcher on GNU/Clang platforms On Windows the ccache MSVC launcher hangs CMake's compiler detection, stalling the wheel build at "Building wheel ... started" for 45+ minutes with no output (the build-windows job log). Keep ccache for macOS/Ubuntu, where it gives 100% cache hits, but skip the launcher on Windows. --- CMakeLists.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index edae5e4..4482b55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,10 +2,14 @@ cmake_minimum_required(VERSION 3.26) # Wrap the compiler in ccache when it's available so the repeated libbson # build in CI is served from the cache. Must be set before project() so the -# launcher applies to the C compiler (and the FetchContent sub-build). -find_program(CCACHE_PROGRAM ccache) -if(CCACHE_PROGRAM) - set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") +# launcher applies to the C compiler (and the FetchContent sub-build). Only +# enable it on GNU/Clang platforms: ccache's MSVC launcher hangs CMake's +# compiler detection on Windows. +if(NOT WIN32) + find_program(CCACHE_PROGRAM ccache) + if(CCACHE_PROGRAM) + set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") + endif() endif() project(bsonjs LANGUAGES C) From 9d4636d17a81ace05873199330228183305b592b Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 16:06:56 -0500 Subject: [PATCH 44/69] Do not install ccache on Windows builds ccache is only used as a compiler launcher on GNU/Clang platforms; its MSVC launcher hangs CMake's compiler detection on Windows. Stop installing it there so the Windows jobs don't pay the choco install for an unused tool. --- .github/actions/setup-ccache/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup-ccache/action.yml b/.github/actions/setup-ccache/action.yml index e6dfc83..e72e274 100644 --- a/.github/actions/setup-ccache/action.yml +++ b/.github/actions/setup-ccache/action.yml @@ -36,12 +36,12 @@ runs: - name: Install ccache shell: bash run: | + # Only used as a compiler launcher on GNU/Clang; ccache's MSVC launcher + # hangs CMake's compiler detection on Windows, so skip it there. if [ "$RUNNER_OS" = "Linux" ]; then sudo apt-get install -y ccache elif [ "$RUNNER_OS" = "macOS" ]; then brew install ccache - elif [ "$RUNNER_OS" = "Windows" ]; then - choco install ccache --no-progress -y fi - name: Restore/save ccache From ffc7425996e08e73bf7a6cd6302a98ba579ce649 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 16:45:01 -0500 Subject: [PATCH 45/69] Make the mongo-c-driver download robust against CI stalls CI runners (especially Windows) can stall for 20+ minutes on the FetchContent download of the mongo-c-driver tarball with no output. Download with hard timeouts (TIMEOUT/INACTIVITY_TIMEOUT) and retries so a stall fails fast instead of hanging the build. Also cache the release tarball via a setup-mongo-c-driver action and feed it to CMake through BSONJS_MCD_TARBALL, so later runs skip the download entirely. --- .../actions/setup-mongo-c-driver/action.yml | 25 +++++++++++++ .github/workflows/test.yml | 6 +++ .gitignore | 3 ++ CMakeLists.txt | 37 +++++++++++-------- 4 files changed, 56 insertions(+), 15 deletions(-) create mode 100644 .github/actions/setup-mongo-c-driver/action.yml diff --git a/.github/actions/setup-mongo-c-driver/action.yml b/.github/actions/setup-mongo-c-driver/action.yml new file mode 100644 index 0000000..fc2c467 --- /dev/null +++ b/.github/actions/setup-mongo-c-driver/action.yml @@ -0,0 +1,25 @@ +name: Setup mongo-c-driver +description: "Cache and fetch the mongo-c-driver release tarball used by FetchContent" +runs: + using: "composite" + steps: + - name: Restore/save mongo-c-driver tarball + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/.mongo-c-driver + key: mcd-2.5.3-${{ runner.os }}-${{ github.sha }} + restore-keys: mcd-2.5.3-${{ runner.os }}- + + - name: Ensure mongo-c-driver tarball is present + shell: bash + run: | + mkdir -p "$GITHUB_WORKSPACE/.mongo-c-driver" + if [ ! -f "$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" ]; then + curl -fsSL \ + --retry 3 \ + --connect-timeout 30 \ + --max-time 300 \ + -o "$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" \ + https://github.com/mongodb/mongo-c-driver/archive/refs/tags/2.5.3.tar.gz + fi + echo "BSONJS_MCD_TARBALL=$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" >> "$GITHUB_ENV" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b0655cc..2ec3fcf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,6 +35,8 @@ jobs: with: key: ccache-${{ runner.os }}-wheel-${{ github.sha }} restore-keys: ccache-${{ runner.os }}-wheel- + - name: Set up mongo-c-driver + uses: ./.github/actions/setup-mongo-c-driver - name: Build wheel run: python -m pip wheel --no-deps -w dist . - name: Show ccache stats @@ -59,6 +61,8 @@ jobs: with: key: ccache-${{ runner.os }}-wheel-${{ github.sha }} restore-keys: ccache-${{ runner.os }}-wheel- + - name: Set up mongo-c-driver + uses: ./.github/actions/setup-mongo-c-driver - name: Build wheel run: python -m pip wheel --no-deps -w dist . - name: Show ccache stats @@ -83,6 +87,8 @@ jobs: with: key: ccache-${{ runner.os }}-wheel-${{ github.sha }} restore-keys: ccache-${{ runner.os }}-wheel- + - name: Set up mongo-c-driver + uses: ./.github/actions/setup-mongo-c-driver - name: Build wheel run: python -m pip wheel --no-deps -w dist . - name: Show ccache stats diff --git a/.gitignore b/.gitignore index 663c87a..b450389 100644 --- a/.gitignore +++ b/.gitignore @@ -133,3 +133,6 @@ ENV/ # Local checkout of mongo-c-driver mongo-c-driver/ + +# CI cache of the mongo-c-driver release tarball +.mongo-c-driver/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 4482b55..4076f9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,23 +24,30 @@ set(_package_version "${CMAKE_MATCH_1}") # --- libbson static library via FetchContent --- include(FetchContent) -# A git clone of mongo-c-driver hangs on the Windows CI runners (no output for -# hours), and a bare tarball URL can hit transient HTTP 504s. Download the -# release tarball once with retries and feed the local archive to FetchContent, -# which is deterministic on every platform. +# A git clone of mongo-c-driver hangs on the Windows CI runners, and a bare +# tarball URL can hang/stall (transient 504s) with no default download timeout. +# Prefer a cached archive when CI provides one (BSONJS_MCD_TARBALL); otherwise +# download with retries and hard timeouts so a stalled connection fails fast +# instead of hanging the build for tens of minutes. set(_mcd_url "https://github.com/mongodb/mongo-c-driver/archive/refs/tags/2.5.3.tar.gz") set(_mcd_archive "${CMAKE_CURRENT_BINARY_DIR}/mongo-c-driver-2.5.3.tar.gz") -file(DOWNLOAD - "${_mcd_url}" - "${_mcd_archive}" - TLS_VERIFY ON - RETRY_COUNT 3 - RETRY_DELAY 5 - STATUS _mcd_status -) -list(GET _mcd_status 0 _mcd_code) -if(NOT _mcd_code EQUAL 0) - message(FATAL_ERROR "Failed to download mongo-c-driver: ${_mcd_status}") +if(DEFINED ENV{BSONJS_MCD_TARBALL} AND EXISTS "$ENV{BSONJS_MCD_TARBALL}") + set(_mcd_archive "$ENV{BSONJS_MCD_TARBALL}") +else() + file(DOWNLOAD + "${_mcd_url}" + "${_mcd_archive}" + TLS_VERIFY ON + TIMEOUT 300 + INACTIVITY_TIMEOUT 120 + RETRY_COUNT 3 + RETRY_DELAY 5 + STATUS _mcd_status + ) + list(GET _mcd_status 0 _mcd_code) + if(NOT _mcd_code EQUAL 0) + message(FATAL_ERROR "Failed to download mongo-c-driver: ${_mcd_status}") + endif() endif() FetchContent_Declare( From 36d30aacdc2f33774d2c0262c010d2fb4f5c9003 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 16:50:52 -0500 Subject: [PATCH 46/69] Normalize Windows paths for the cached mongo-c-driver archive The BSONJS_MCD_TARBALL override uses a Windows path with backslashes, which broke the FetchContent subbuild's generated CMakeLists.txt (syntax error). Convert it with file(TO_CMAKE_PATH) and only skip the download when the normalized path actually exists. --- CMakeLists.txt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4076f9e..8bee4c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,9 +31,15 @@ include(FetchContent) # instead of hanging the build for tens of minutes. set(_mcd_url "https://github.com/mongodb/mongo-c-driver/archive/refs/tags/2.5.3.tar.gz") set(_mcd_archive "${CMAKE_CURRENT_BINARY_DIR}/mongo-c-driver-2.5.3.tar.gz") -if(DEFINED ENV{BSONJS_MCD_TARBALL} AND EXISTS "$ENV{BSONJS_MCD_TARBALL}") - set(_mcd_archive "$ENV{BSONJS_MCD_TARBALL}") -else() +if(DEFINED ENV{BSONJS_MCD_TARBALL}) + # Normalize Windows backslash paths, which would otherwise break the + # FetchContent subbuild's generated CMakeLists.txt. + file(TO_CMAKE_PATH "$ENV{BSONJS_MCD_TARBALL}" _mcd_cached) + if(EXISTS "${_mcd_cached}") + set(_mcd_archive "${_mcd_cached}") + endif() +endif() +if(NOT EXISTS "${_mcd_archive}") file(DOWNLOAD "${_mcd_url}" "${_mcd_archive}" From aeb18589461331b9a1f9fb07aa1d740291277ef6 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 17:50:47 -0500 Subject: [PATCH 47/69] Pre-fetch the mongo-c-driver tarball and fail fast if it's unavailable Hardens the download so CI never silently falls back to the stalling codeload endpoint mid-build. setup-mongo-c-driver now fails the job (set -euo pipefail + test -s) if the release tarball can't be fetched within the retry/timeout budget, and is wired into the dist.yml wheel build too so the host builds (macOS/Windows) pre-fetch it. CMakeLists still prefers the pre-fetched archive; the timeout-bounded download stays as a fallback for dist.yml Linux containers and sdist/local builds, which can't run the action but don't stall on codeload. --- .github/actions/setup-mongo-c-driver/action.yml | 5 ++++- .github/workflows/dist.yml | 2 ++ CMakeLists.txt | 8 ++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/actions/setup-mongo-c-driver/action.yml b/.github/actions/setup-mongo-c-driver/action.yml index fc2c467..fed05d4 100644 --- a/.github/actions/setup-mongo-c-driver/action.yml +++ b/.github/actions/setup-mongo-c-driver/action.yml @@ -13,13 +13,16 @@ runs: - name: Ensure mongo-c-driver tarball is present shell: bash run: | + set -euo pipefail mkdir -p "$GITHUB_WORKSPACE/.mongo-c-driver" if [ ! -f "$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" ]; then curl -fsSL \ --retry 3 \ --connect-timeout 30 \ - --max-time 300 \ + --max-time 180 \ -o "$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" \ https://github.com/mongodb/mongo-c-driver/archive/refs/tags/2.5.3.tar.gz fi + test -s "$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" \ + || { echo "::error::mongo-c-driver tarball is missing; cannot build" >&2; exit 1; } echo "BSONJS_MCD_TARBALL=$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" >> "$GITHUB_ENV" diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index d129be0..9d3b4e0 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -39,6 +39,8 @@ jobs: with: key: ccache-${{ runner.os }}-${{ github.sha }} restore-keys: ccache-${{ runner.os }}- + - name: Set up mongo-c-driver + uses: ./.github/actions/setup-mongo-c-driver - uses: pypa/cibuildwheel@4726cd35bb13f7bde50cf2761f2499ac7b3aa32c # v4.1.1 - uses: actions/upload-artifact@v7 with: diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bee4c9..69c200b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,10 +24,10 @@ set(_package_version "${CMAKE_MATCH_1}") # --- libbson static library via FetchContent --- include(FetchContent) -# A git clone of mongo-c-driver hangs on the Windows CI runners, and a bare -# tarball URL can hang/stall (transient 504s) with no default download timeout. -# Prefer a cached archive when CI provides one (BSONJS_MCD_TARBALL); otherwise -# download with retries and hard timeouts so a stalled connection fails fast +# The mongo-c-driver release tarball is pre-fetched by CI +# (.github/actions/setup-mongo-c-driver) and passed in via BSONJS_MCD_TARBALL, +# so the CI build phase is download-free. Otherwise (dist.yml wheels, sdist, +# local builds) download with hard timeouts so a stalled connection fails fast # instead of hanging the build for tens of minutes. set(_mcd_url "https://github.com/mongodb/mongo-c-driver/archive/refs/tags/2.5.3.tar.gz") set(_mcd_archive "${CMAKE_CURRENT_BINARY_DIR}/mongo-c-driver-2.5.3.tar.gz") From 50ec8613eb845b72432d7647d84e5e986d529fdc Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 19:16:12 -0500 Subject: [PATCH 48/69] Instrument the mongo-c-driver fetch and FetchContent with timing markers GitHub does not stream the in-build CMake output until a job finishes, so a stall is invisible. Add [bsonjs] phase markers (configure begin, archive source + size, download, FetchContent_MakeAvailable begin/done) with an elapsed counter to CMakeLists, and log cache-vs-download in setup-mongo-c-driver. The next run's log will show exactly which phase consumes the time on the hosted runner. --- .../actions/setup-mongo-c-driver/action.yml | 13 +++++++---- CMakeLists.txt | 22 +++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/.github/actions/setup-mongo-c-driver/action.yml b/.github/actions/setup-mongo-c-driver/action.yml index fed05d4..90a2c6b 100644 --- a/.github/actions/setup-mongo-c-driver/action.yml +++ b/.github/actions/setup-mongo-c-driver/action.yml @@ -15,14 +15,19 @@ runs: run: | set -euo pipefail mkdir -p "$GITHUB_WORKSPACE/.mongo-c-driver" - if [ ! -f "$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" ]; then + TARBALL="$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" + if [ -s "$TARBALL" ]; then + echo "[bsonjs] mongo-c-driver tarball restored from cache ($(stat -c%s "$TARBALL" 2>/dev/null || stat -f%z "$TARBALL") bytes)" + else + echo "[bsonjs] mongo-c-driver tarball not cached; downloading from codeload..." curl -fsSL \ --retry 3 \ --connect-timeout 30 \ --max-time 180 \ - -o "$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" \ + -o "$TARBALL" \ https://github.com/mongodb/mongo-c-driver/archive/refs/tags/2.5.3.tar.gz + echo "[bsonjs] mongo-c-driver tarball downloaded ($(stat -c%s "$TARBALL" 2>/dev/null || stat -f%z "$TARBALL") bytes)" fi - test -s "$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" \ + test -s "$TARBALL" \ || { echo "::error::mongo-c-driver tarball is missing; cannot build" >&2; exit 1; } - echo "BSONJS_MCD_TARBALL=$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" >> "$GITHUB_ENV" + echo "BSONJS_MCD_TARBALL=$TARBALL" >> "$GITHUB_ENV" diff --git a/CMakeLists.txt b/CMakeLists.txt index 69c200b..9aa4e8d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,17 @@ set(_package_version "${CMAKE_MATCH_1}") # --- libbson static library via FetchContent --- include(FetchContent) +# Small timing helper: logs a phase marker with an elapsed counter so a +# completed or cancelled CI log shows where the build time is going. +string(TIMESTAMP _bsonjs_last "%s") +function(bsonjs_mark msg) + string(TIMESTAMP _now "%s") + math(EXPR _elapsed "${_now} - ${_bsonjs_last}") + message(STATUS "[bsonjs] ${msg} (+${_elapsed}s)") + set(_bsonjs_last "${_now}" PARENT_SCOPE) +endfunction() +bsonjs_mark("configure begin") + # The mongo-c-driver release tarball is pre-fetched by CI # (.github/actions/setup-mongo-c-driver) and passed in via BSONJS_MCD_TARBALL, # so the CI build phase is download-free. Otherwise (dist.yml wheels, sdist, @@ -39,7 +50,10 @@ if(DEFINED ENV{BSONJS_MCD_TARBALL}) set(_mcd_archive "${_mcd_cached}") endif() endif() +message(STATUS "[bsonjs] mongo-c-driver BSONJS_MCD_TARBALL=$ENV{BSONJS_MCD_TARBALL}") +message(STATUS "[bsonjs] mongo-c-driver archive=${_mcd_archive}") if(NOT EXISTS "${_mcd_archive}") + bsonjs_mark("archive missing -> downloading") file(DOWNLOAD "${_mcd_url}" "${_mcd_archive}" @@ -54,6 +68,10 @@ if(NOT EXISTS "${_mcd_archive}") if(NOT _mcd_code EQUAL 0) message(FATAL_ERROR "Failed to download mongo-c-driver: ${_mcd_status}") endif() + bsonjs_mark("archive downloaded") +else() + file(SIZE "${_mcd_archive}" _mcd_size) + message(STATUS "[bsonjs] mongo-c-driver archive size=${_mcd_size} bytes (cached)") endif() FetchContent_Declare( @@ -62,6 +80,10 @@ FetchContent_Declare( DOWNLOAD_EXTRACT_TIMESTAMP TRUE ) +bsonjs_mark("FetchContent_MakeAvailable begin") +FetchContent_MakeAvailable(mongo-c-driver) +bsonjs_mark("FetchContent_MakeAvailable done") + # We only need libbson, not libmongoc, which requires a TLS/crypto library. # Turning mongoc off also keeps the build C-only and the driver's dependencies # out of the picture. Build libbson as a static library. From 2fc905da3b0f1dcfd5d86a189ffce99a8a88377d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 19:56:13 -0500 Subject: [PATCH 49/69] Build wheels with --no-build-isolation to rule out isolation overhead The pip build-isolation env (fresh venv + tmp dir re-installing scikit-build-core/cmake/ninja each run) is a suspect for the slow Windows build. Pre-install the build deps and build with --no-build-isolation, so the wheel builds in the repository's own build/ dir. Verified on the Windows host: the cp311-abi3 wheel builds in ~18s. Also guard the ccache stats step off Windows, where ccache is no longer installed. --- .github/workflows/test.yml | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2ec3fcf..54884ea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,9 +37,12 @@ jobs: restore-keys: ccache-${{ runner.os }}-wheel- - name: Set up mongo-c-driver uses: ./.github/actions/setup-mongo-c-driver + - name: Install build dependencies + run: python -m pip install "scikit-build-core>=0.10" "cmake>=3.26" ninja - name: Build wheel - run: python -m pip wheel --no-deps -w dist . + run: python -m pip wheel --no-build-isolation --no-deps -w dist . - name: Show ccache stats + if: runner.os != 'Windows' run: ccache -s - uses: actions/upload-artifact@v7 with: @@ -63,9 +66,12 @@ jobs: restore-keys: ccache-${{ runner.os }}-wheel- - name: Set up mongo-c-driver uses: ./.github/actions/setup-mongo-c-driver + - name: Install build dependencies + run: python -m pip install "scikit-build-core>=0.10" "cmake>=3.26" ninja - name: Build wheel - run: python -m pip wheel --no-deps -w dist . + run: python -m pip wheel --no-build-isolation --no-deps -w dist . - name: Show ccache stats + if: runner.os != 'Windows' run: ccache -s - uses: actions/upload-artifact@v7 with: @@ -89,9 +95,12 @@ jobs: restore-keys: ccache-${{ runner.os }}-wheel- - name: Set up mongo-c-driver uses: ./.github/actions/setup-mongo-c-driver + - name: Install build dependencies + run: python -m pip install "scikit-build-core>=0.10" "cmake>=3.26" ninja - name: Build wheel - run: python -m pip wheel --no-deps -w dist . + run: python -m pip wheel --no-build-isolation --no-deps -w dist . - name: Show ccache stats + if: runner.os != 'Windows' run: ccache -s - uses: actions/upload-artifact@v7 with: From 06512e695904c7f03c943f87160290c2850ef3d6 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 20:21:05 -0500 Subject: [PATCH 50/69] Build only libbson: apply the minimal-build options before FetchContent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ENABLE_MONGOC/ENABLE_SHARED/ENABLE_STATIC options were set AFTER FetchContent_MakeAvailable, so they were never honored and mongo-c-driver defaulted to building libmongoc (plus zlib, utf8proc, SASL, and the shared variants) — ~300 C files instead of the ~30 for libbson. That is why the GitHub-hosted Windows build stalls (a 10x MSVC workload) and why the Linux dist wheel fails with "MONGODB-AWS requires a crypto library". Move the options above the single MakeAvailable so the subproject configures libbson-only, and pin the same defines in pyproject (cmake.define) so every build path configures the same minimal build. --- CMakeLists.txt | 12 ++++++------ pyproject.toml | 13 +++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9aa4e8d..855a1dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,20 +80,20 @@ FetchContent_Declare( DOWNLOAD_EXTRACT_TIMESTAMP TRUE ) -bsonjs_mark("FetchContent_MakeAvailable begin") -FetchContent_MakeAvailable(mongo-c-driver) -bsonjs_mark("FetchContent_MakeAvailable done") - # We only need libbson, not libmongoc, which requires a TLS/crypto library. -# Turning mongoc off also keeps the build C-only and the driver's dependencies -# out of the picture. Build libbson as a static library. +# These must be set BEFORE FetchContent_MakeAvailable so they are honored when +# the subproject configures; otherwise libmongoc (and its zlib/utf8proc/SASL/ +# shared-variant dependencies) get built, which is slow and fails without TLS. set(ENABLE_MONGOC OFF) set(ENABLE_SHARED OFF) set(ENABLE_STATIC ON) # The driver builds a C++ test gated on BUILD_TESTING; keep the build C-only. set(BUILD_TESTING OFF) set(ENABLE_TESTS OFF) + +bsonjs_mark("FetchContent_MakeAvailable begin") FetchContent_MakeAvailable(mongo-c-driver) +bsonjs_mark("FetchContent_MakeAvailable done") # bson:static (alias bson_static) propagates BSON_STATIC already, but the # driver does not export its include dirs on the target, so add them here so diff --git a/pyproject.toml b/pyproject.toml index ab44e7b..1549c43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,19 @@ wheel.py-api = "cp311" # ccache (no cross-run hits). Building here keeps the paths stable. build-dir = "build" +[tool.scikit-build.cmake.define] +# Build only libbson (static), never libmongoc. Unset, mongo-c-driver defaults +# mongoc/shared ON, pulling in TLS/SASL/zlib/utf8proc and the shared variants, +# which is slow and fails without a crypto library. These are set here as CMake +# cache vars so every build path (cibuildwheel + pip wheel) configures the same +# minimal, libbson-only build. +ENABLE_MONGOC = "OFF" +ENABLE_SHARED = "OFF" +ENABLE_STATIC = "ON" +BUILD_TESTING = "OFF" +ENABLE_TESTS = "OFF" +ENABLE_EXAMPLES = "OFF" + [tool.cibuildwheel] test-command = "pytest {package}/test" test-extras = ["test"] From 67634c79d7ceed65d30835fca4681a66682a4c39 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 14 Sep 2026 20:58:28 -0500 Subject: [PATCH 51/69] Exclude the workspace from Defender to speed up MSVC compilation GitHub-hosted Windows real-time MSVC compilation is throttled by Defender, which is the classic cause of 10-50x slowdowns. Exclude the checkout from Defender scanning before the build. Combined with building only libbson, this targets the residual slow compile on hosted runners. --- .github/actions/setup-mongo-c-driver/action.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/actions/setup-mongo-c-driver/action.yml b/.github/actions/setup-mongo-c-driver/action.yml index 90a2c6b..2e11ac2 100644 --- a/.github/actions/setup-mongo-c-driver/action.yml +++ b/.github/actions/setup-mongo-c-driver/action.yml @@ -3,6 +3,15 @@ description: "Cache and fetch the mongo-c-driver release tarball used by FetchCo runs: using: "composite" steps: + - name: Exclude the workspace from Windows Defender + if: runner.os == 'Windows' + shell: powershell + run: | + # Real-time MSVC compilation on GitHub-hosted Windows is throttled by + # Defender; excluding the workspace is the standard, big win. + Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE" -ErrorAction SilentlyContinue + Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\build" -ErrorAction SilentlyContinue + - name: Restore/save mongo-c-driver tarball uses: actions/cache@v4 with: From e3e232d976622121476e03abe216841f577f305e Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 05:42:00 -0500 Subject: [PATCH 52/69] Add a toolchain-detection step to confirm MSVC vs MinGW on runners Diagnostic: print whether cl/gcc are on PATH and which C compiler CMake picks, so we can confirm the GitHub-hosted Windows runner is building with MSVC and not MinGW-w64 gcc (which is preinstalled alongside Visual Studio). --- .github/workflows/test.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 54884ea..86109de 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,6 +39,15 @@ jobs: uses: ./.github/actions/setup-mongo-c-driver - name: Install build dependencies run: python -m pip install "scikit-build-core>=0.10" "cmake>=3.26" ninja + - name: Detect toolchain + shell: bash + run: | + echo "--- which cl ---"; command -v cl 2>/dev/null || echo "cl NOT on PATH" + echo "--- which gcc ---"; command -v gcc 2>/dev/null || echo "gcc NOT on PATH" + echo "--- CMAKE_GENERATOR=${CMAKE_GENERATOR:-unset} ---" + rm -rf ccdiag && mkdir -p ccdiag + printf 'cmake_minimum_required(VERSION 3.26)\nproject(diag C)\n' > ccdiag/CMakeLists.txt + cmake -S ccdiag -B ccdiag/build 2>&1 | grep -iE "generator|C compiler identification|cl\\.exe|gcc|Visual Studio|MinGW" || true - name: Build wheel run: python -m pip wheel --no-build-isolation --no-deps -w dist . - name: Show ccache stats @@ -68,6 +77,15 @@ jobs: uses: ./.github/actions/setup-mongo-c-driver - name: Install build dependencies run: python -m pip install "scikit-build-core>=0.10" "cmake>=3.26" ninja + - name: Detect toolchain + shell: bash + run: | + echo "--- which cl ---"; command -v cl 2>/dev/null || echo "cl NOT on PATH" + echo "--- which gcc ---"; command -v gcc 2>/dev/null || echo "gcc NOT on PATH" + echo "--- CMAKE_GENERATOR=${CMAKE_GENERATOR:-unset} ---" + rm -rf ccdiag && mkdir -p ccdiag + printf 'cmake_minimum_required(VERSION 3.26)\nproject(diag C)\n' > ccdiag/CMakeLists.txt + cmake -S ccdiag -B ccdiag/build 2>&1 | grep -iE "generator|C compiler identification|cl\\.exe|gcc|Visual Studio|MinGW" || true - name: Build wheel run: python -m pip wheel --no-build-isolation --no-deps -w dist . - name: Show ccache stats @@ -97,6 +115,15 @@ jobs: uses: ./.github/actions/setup-mongo-c-driver - name: Install build dependencies run: python -m pip install "scikit-build-core>=0.10" "cmake>=3.26" ninja + - name: Detect toolchain + shell: bash + run: | + echo "--- which cl ---"; command -v cl 2>/dev/null || echo "cl NOT on PATH" + echo "--- which gcc ---"; command -v gcc 2>/dev/null || echo "gcc NOT on PATH" + echo "--- CMAKE_GENERATOR=${CMAKE_GENERATOR:-unset} ---" + rm -rf ccdiag && mkdir -p ccdiag + printf 'cmake_minimum_required(VERSION 3.26)\nproject(diag C)\n' > ccdiag/CMakeLists.txt + cmake -S ccdiag -B ccdiag/build 2>&1 | grep -iE "generator|C compiler identification|cl\\.exe|gcc|Visual Studio|MinGW" || true - name: Build wheel run: python -m pip wheel --no-build-isolation --no-deps -w dist . - name: Show ccache stats From 8279038dcac1ae1c4cfed869194085b63e417db4 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 05:50:05 -0500 Subject: [PATCH 53/69] Force MSVC in the Windows wheel build (was defaulting to MinGW) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Diagnosis: scikit-build-core uses Ninja, which picks the compiler from PATH. On GitHub-hosted Windows `cl` is not on PATH and C:\\mingw64\\bin\\gcc is, so the build was compiling with MinGW gcc (GNU 12.2.0), not MSVC — a different toolchain that behaved unlike the box. Set up the MSVC dev environment (vcvars64 via vswhere) before pip wheel so cl.exe is on PATH and scikit-build-core builds a proper MSVC extension. --- .github/workflows/test.yml | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 86109de..b7032c0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,15 +39,6 @@ jobs: uses: ./.github/actions/setup-mongo-c-driver - name: Install build dependencies run: python -m pip install "scikit-build-core>=0.10" "cmake>=3.26" ninja - - name: Detect toolchain - shell: bash - run: | - echo "--- which cl ---"; command -v cl 2>/dev/null || echo "cl NOT on PATH" - echo "--- which gcc ---"; command -v gcc 2>/dev/null || echo "gcc NOT on PATH" - echo "--- CMAKE_GENERATOR=${CMAKE_GENERATOR:-unset} ---" - rm -rf ccdiag && mkdir -p ccdiag - printf 'cmake_minimum_required(VERSION 3.26)\nproject(diag C)\n' > ccdiag/CMakeLists.txt - cmake -S ccdiag -B ccdiag/build 2>&1 | grep -iE "generator|C compiler identification|cl\\.exe|gcc|Visual Studio|MinGW" || true - name: Build wheel run: python -m pip wheel --no-build-isolation --no-deps -w dist . - name: Show ccache stats @@ -77,15 +68,6 @@ jobs: uses: ./.github/actions/setup-mongo-c-driver - name: Install build dependencies run: python -m pip install "scikit-build-core>=0.10" "cmake>=3.26" ninja - - name: Detect toolchain - shell: bash - run: | - echo "--- which cl ---"; command -v cl 2>/dev/null || echo "cl NOT on PATH" - echo "--- which gcc ---"; command -v gcc 2>/dev/null || echo "gcc NOT on PATH" - echo "--- CMAKE_GENERATOR=${CMAKE_GENERATOR:-unset} ---" - rm -rf ccdiag && mkdir -p ccdiag - printf 'cmake_minimum_required(VERSION 3.26)\nproject(diag C)\n' > ccdiag/CMakeLists.txt - cmake -S ccdiag -B ccdiag/build 2>&1 | grep -iE "generator|C compiler identification|cl\\.exe|gcc|Visual Studio|MinGW" || true - name: Build wheel run: python -m pip wheel --no-build-isolation --no-deps -w dist . - name: Show ccache stats @@ -115,17 +97,16 @@ jobs: uses: ./.github/actions/setup-mongo-c-driver - name: Install build dependencies run: python -m pip install "scikit-build-core>=0.10" "cmake>=3.26" ninja - - name: Detect toolchain - shell: bash - run: | - echo "--- which cl ---"; command -v cl 2>/dev/null || echo "cl NOT on PATH" - echo "--- which gcc ---"; command -v gcc 2>/dev/null || echo "gcc NOT on PATH" - echo "--- CMAKE_GENERATOR=${CMAKE_GENERATOR:-unset} ---" - rm -rf ccdiag && mkdir -p ccdiag - printf 'cmake_minimum_required(VERSION 3.26)\nproject(diag C)\n' > ccdiag/CMakeLists.txt - cmake -S ccdiag -B ccdiag/build 2>&1 | grep -iE "generator|C compiler identification|cl\\.exe|gcc|Visual Studio|MinGW" || true - name: Build wheel - run: python -m pip wheel --no-build-isolation --no-deps -w dist . + shell: pwsh + run: | + # Force MSVC: with cl off the default Ninja toolchain, scikit-build-core + # falls back to MinGW gcc, producing a GNU-built wheel. Use vswhere to + # locate vcvars64.bat and set up the MSVC environment so cl is on PATH. + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + $vcvars = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -find VC\Auxiliary\Build\vcvars64.bat | Select-Object -First 1 + Write-Host "vcvars64.bat: $vcvars" + cmd /c "call `"$vcvars`" x64 >nul && python -m pip wheel --no-build-isolation --no-deps -w dist ." - name: Show ccache stats if: runner.os != 'Windows' run: ccache -s From 4868f00bc5764a68bf0216310b800a7d5222a1cf Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 06:59:47 -0500 Subject: [PATCH 54/69] Build libbson via meson-python instead of scikit-build-core scikit-build-core's Ninja build picked MinGW gcc on Windows runners, producing a GNU wheel instead of an MSVC one. Meson with --vsenv forces the Visual Studio toolchain. The Meson build compiles libbson from the vendored mongo-c-driver source and tags the module cp311-abi3. --- .../actions/setup-mongo-c-driver/action.yml | 10 +- .github/workflows/test.yml | 16 +- bson/meson.build | 16 ++ meson.build | 151 ++++++++++++++++++ meson_options.txt | 2 + pyproject.toml | 31 ++-- 6 files changed, 191 insertions(+), 35 deletions(-) create mode 100644 bson/meson.build create mode 100644 meson.build create mode 100644 meson_options.txt diff --git a/.github/actions/setup-mongo-c-driver/action.yml b/.github/actions/setup-mongo-c-driver/action.yml index 2e11ac2..38deea8 100644 --- a/.github/actions/setup-mongo-c-driver/action.yml +++ b/.github/actions/setup-mongo-c-driver/action.yml @@ -19,12 +19,13 @@ runs: key: mcd-2.5.3-${{ runner.os }}-${{ github.sha }} restore-keys: mcd-2.5.3-${{ runner.os }}- - - name: Ensure mongo-c-driver tarball is present + - name: Ensure mongo-c-driver tarball is present and extracted shell: bash run: | set -euo pipefail mkdir -p "$GITHUB_WORKSPACE/.mongo-c-driver" TARBALL="$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" + SRCDIR="$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3" if [ -s "$TARBALL" ]; then echo "[bsonjs] mongo-c-driver tarball restored from cache ($(stat -c%s "$TARBALL" 2>/dev/null || stat -f%z "$TARBALL") bytes)" else @@ -39,4 +40,11 @@ runs: fi test -s "$TARBALL" \ || { echo "::error::mongo-c-driver tarball is missing; cannot build" >&2; exit 1; } + # Meson needs the extracted tree; extract if not already present (the + # extracted dir is also cached, so later runs skip this). + if [ ! -d "$SRCDIR" ]; then + tar -xzf "$TARBALL" -C "$GITHUB_WORKSPACE/.mongo-c-driver" + fi + test -f "$SRCDIR/src/libbson/src/bson/config.h.in" \ + || { echo "::error::mongo-c-driver extraction failed" >&2; exit 1; } echo "BSONJS_MCD_TARBALL=$TARBALL" >> "$GITHUB_ENV" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b7032c0..deb7bf1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,7 +38,7 @@ jobs: - name: Set up mongo-c-driver uses: ./.github/actions/setup-mongo-c-driver - name: Install build dependencies - run: python -m pip install "scikit-build-core>=0.10" "cmake>=3.26" ninja + run: python -m pip install "meson-python>=0.17" "meson>=1.2" ninja - name: Build wheel run: python -m pip wheel --no-build-isolation --no-deps -w dist . - name: Show ccache stats @@ -67,7 +67,7 @@ jobs: - name: Set up mongo-c-driver uses: ./.github/actions/setup-mongo-c-driver - name: Install build dependencies - run: python -m pip install "scikit-build-core>=0.10" "cmake>=3.26" ninja + run: python -m pip install "meson-python>=0.17" "meson>=1.2" ninja - name: Build wheel run: python -m pip wheel --no-build-isolation --no-deps -w dist . - name: Show ccache stats @@ -96,17 +96,9 @@ jobs: - name: Set up mongo-c-driver uses: ./.github/actions/setup-mongo-c-driver - name: Install build dependencies - run: python -m pip install "scikit-build-core>=0.10" "cmake>=3.26" ninja + run: python -m pip install "meson-python>=0.17" "meson>=1.2" ninja - name: Build wheel - shell: pwsh - run: | - # Force MSVC: with cl off the default Ninja toolchain, scikit-build-core - # falls back to MinGW gcc, producing a GNU-built wheel. Use vswhere to - # locate vcvars64.bat and set up the MSVC environment so cl is on PATH. - $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" - $vcvars = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -find VC\Auxiliary\Build\vcvars64.bat | Select-Object -First 1 - Write-Host "vcvars64.bat: $vcvars" - cmd /c "call `"$vcvars`" x64 >nul && python -m pip wheel --no-build-isolation --no-deps -w dist ." + run: python -m pip wheel --no-build-isolation -Csetup-args=--vsenv --no-deps -w dist . - name: Show ccache stats if: runner.os != 'Windows' run: ccache -s diff --git a/bson/meson.build b/bson/meson.build new file mode 100644 index 0000000..56e528e --- /dev/null +++ b/bson/meson.build @@ -0,0 +1,16 @@ +# Generated libbson headers (config.h, version.h). +# +# libbson's sources do `#include ` and ``, so +# these must land in /bson/ (the root adds to the include +# path). output cannot contain a path segment, so this lives in a `bson/` +# subdir whose build dir is /bson/. +configure_file( + input: mcd_src / 'src' / 'libbson' / 'src' / 'bson' / 'config.h.in', + output: 'config.h', + configuration: conf, +) +configure_file( + input: mcd_src / 'src' / 'libbson' / 'src' / 'bson' / 'version.h.in', + output: 'version.h', + configuration: version_conf, +) diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..fcc019f --- /dev/null +++ b/meson.build @@ -0,0 +1,151 @@ +project( + 'bsonjs', + 'c', + version: '0.8.0', + default_options: ['c_std=c99', 'warning_level=1'], +) + +py = import('python').find_installation(pure: false) +fs = import('fs') +cc = meson.get_compiler('c') +threads = dependency('threads') + +# libbson's bundled mlib uses POSIX clock/thread primitives that get selected +# via _POSIX_C_SOURCE; without it the build fails on POSIX hosts. Windows has +# its own code path (guarded on _WIN32), so leave it out there. +if host_machine.system() != 'windows' + add_project_arguments('-D_POSIX_C_SOURCE=200809L', language: 'c') +endif + +# --- libbson from source ------------------------------------------------ +# Meson has no FetchContent, so the workflow pre-fetches and extracts the +# mongo-c-driver release tarball and points BSONJS_MCD_SRC at it. We compile +# only libbson (static) — never libmongoc — by listing its sources here. +mcd_src = get_option('mongo-c-driver-dir') +if mcd_src == '' + # Fallback: the workflow pre-fetches + extracts the release tarball here. + fallback = meson.project_source_root() / '.mongo-c-driver' / 'mongo-c-driver-2.5.3' + if fs.is_dir(fallback) + mcd_src = fallback + endif +endif +if mcd_src == '' or not fs.is_dir(mcd_src) + error('mongo-c-driver source dir not found. Set -Dmongo-c-driver-dir or extract the tarball to .mongo-c-driver/mongo-c-driver-2.5.3.') +endif + +bson_src_dir = mcd_src / 'src' / 'libbson' / 'src' +common_src_dir = mcd_src / 'src' / 'common' / 'src' + +# Generated headers (config.h, version.h) from the CMake templates. +libbson_major = 2 +libbson_minor = 5 +libbson_patch = 3 +conf = configuration_data() +if host_machine.endian() == 'little' + conf.set('BSON_BYTE_ORDER', 1234) +else + conf.set('BSON_BYTE_ORDER', 4321) +endif +if host_machine.system() == 'windows' + conf.set('BSON_OS', 2) + conf.set('BSON_HAVE_STRINGS_H', 0) + conf.set('BSON_HAVE_STRNLEN', 0) + conf.set('BSON_HAVE_CLOCK_GETTIME', 0) + conf.set('BSON_HAVE_GMTIME_R', 0) + conf.set('BSON_HAVE_RAND_R', 0) + conf.set('BSON_HAVE_TIMESPEC', 0) + conf.set('BSON_HAVE_ALIGNED_ALLOC', 0) + conf.set('BSON_HAVE_STRLCPY', 0) +else + conf.set('BSON_OS', 1) + conf.set('BSON_HAVE_STRINGS_H', 1) + conf.set('BSON_HAVE_STRNLEN', 1) + conf.set('BSON_HAVE_CLOCK_GETTIME', 1) + conf.set('BSON_HAVE_GMTIME_R', 1) + conf.set('BSON_HAVE_RAND_R', 1) + conf.set('BSON_HAVE_TIMESPEC', 1) + # aligned_alloc is C11 and is not declared under -std=c99. + conf.set('BSON_HAVE_ALIGNED_ALLOC', 0) + conf.set('BSON_HAVE_STRLCPY', host_machine.system() == 'darwin') +endif +conf.set('BSON_HAVE_STDBOOL_H', 1) +conf.set('BSON_HAVE_SNPRINTF', 1) +version_conf = configuration_data() +version_conf.set('libbson_VERSION_MAJOR', libbson_major) +version_conf.set('libbson_VERSION_MINOR', libbson_minor) +version_conf.set('libbson_VERSION_PATCH', libbson_patch) +version_conf.set('libbson_VERSION_FULL', '2.5.3') +version_conf.set('libbson_VERSION_PRERELEASE', '') +subdir('bson') +# common-config.h is included by the common/ sources; the build dir is on the +# include path (via inc_flags), so this generated header resolves as . +common_conf = configuration_data() +common_conf.set('MONGOC_ENABLE_DEBUG_ASSERTIONS', 0) +configure_file(input: common_src_dir / 'common-config.h.in', + output: 'common-config.h', configuration: common_conf) + +# --- libbson static library -------------------------------------------- +# Meson rejects absolute source-tree paths in include_directories, and the +# libbson tree lives in .mongo-c-driver/ (or an externally supplied dir), so +# pass the include dirs as -I flags in c_args. +bson_incs = [ + bson_src_dir, + bson_src_dir / 'bson', + common_src_dir, + meson.current_build_dir(), +] +inc_flags = [] +foreach d : bson_incs + inc_flags += '-I' + d +endforeach +libbson_src = [ + common_src_dir / 'common-atomic.c', + common_src_dir / 'common-b64.c', + common_src_dir / 'common-json.c', + common_src_dir / 'common-md5.c', + common_src_dir / 'common-oid.c', + common_src_dir / 'common-string.c', + common_src_dir / 'common-thread.c', + bson_src_dir / 'bson' / 'bson.c', + bson_src_dir / 'bson' / 'bson-bcon.c', + bson_src_dir / 'bson' / 'bson-clock.c', + bson_src_dir / 'bson' / 'bson-context.c', + bson_src_dir / 'bson' / 'bson-decimal128.c', + bson_src_dir / 'bson' / 'bson-iso8601.c', + bson_src_dir / 'bson' / 'bson-iter.c', + bson_src_dir / 'bson' / 'bson-json.c', + bson_src_dir / 'bson' / 'bson-keys.c', + bson_src_dir / 'bson' / 'bson-oid.c', + bson_src_dir / 'bson' / 'bson-reader.c', + bson_src_dir / 'bson' / 'bson-string.c', + bson_src_dir / 'bson' / 'bson-timegm.c', + bson_src_dir / 'bson' / 'bson-utf8.c', + bson_src_dir / 'bson' / 'bson-value.c', + bson_src_dir / 'bson' / 'bson-vector.c', + bson_src_dir / 'bson' / 'bson-version-functions.c', + bson_src_dir / 'bson' / 'error.c', + bson_src_dir / 'bson' / 'memory.c', + bson_src_dir / 'bson' / 'validate.c', + bson_src_dir / 'jsonsl' / 'jsonsl.c', +] +libbson = static_library('bson', + libbson_src, + c_args: ['-DBSON_COMPILATION', '-DBSON_STATIC', '-DJSONSL_PARSE_NAN'] + inc_flags, +) + +# --- Python extension module (Limited API / abi3) ------------------------ +# bsonjs.__version__ comes from this macro; keep it in sync with the pyproject +# version (meson.project_version() does not see meson-python's override). +bsonjs_version = run_command(py, '-c', + 'import re,pathlib;print(re.search(r\'version\\s*=\\s*"([^"]+)"\',pathlib.Path("pyproject.toml").read_text()).group(1))', + check: false).stdout().strip() + +py.extension_module( + 'bsonjs', + 'bsonjs/bsonjs.c', + c_args: ['-DBSON_STATIC', '-DBSONJS_VERSION="@0@"'.format(bsonjs_version)] + inc_flags, + link_with: libbson, + link_args: host_machine.system() == 'windows' ? ['ws2_32'] : [], + install: true, + limited_api: '3.11', +) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..141b5ec --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,2 @@ +option('mongo-c-driver-dir', type: 'string', value: '', + description: 'Path to the extracted mongo-c-driver 2.5.3 source tree') diff --git a/pyproject.toml b/pyproject.toml index 1549c43..42bde3d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] -requires = ["scikit-build-core>=0.10", "cmake>=3.26", "ninja"] -build-backend = "scikit_build_core.build" +requires = ["meson-python>=0.17", "meson>=1.2", "ninja"] +build-backend = "mesonpy" [project] name = "python-bsonjs" @@ -39,26 +39,10 @@ Homepage = "https://github.com/mongodb-labs/python-bsonjs" [project.optional-dependencies] test = ["pymongo>=4", "pytest"] -[tool.scikit-build] -wheel.packages = [] -wheel.py-api = "cp311" -# Deterministic build dir: pip wheel's default build isolation uses a fresh -# temp dir each run, which changes the fetched mongo-c-driver paths and defeats -# ccache (no cross-run hits). Building here keeps the paths stable. -build-dir = "build" - -[tool.scikit-build.cmake.define] -# Build only libbson (static), never libmongoc. Unset, mongo-c-driver defaults -# mongoc/shared ON, pulling in TLS/SASL/zlib/utf8proc and the shared variants, -# which is slow and fails without a crypto library. These are set here as CMake -# cache vars so every build path (cibuildwheel + pip wheel) configures the same -# minimal, libbson-only build. -ENABLE_MONGOC = "OFF" -ENABLE_SHARED = "OFF" -ENABLE_STATIC = "ON" -BUILD_TESTING = "OFF" -ENABLE_TESTS = "OFF" -ENABLE_EXAMPLES = "OFF" +[tool.meson-python] +# Build a single stable-ABI (abi3) wheel: meson-python tags it cp311-abi3 and +# Meson auto-detects MSVC on Windows (no MinGW fallback). +limited-api = true [tool.cibuildwheel] test-command = "pytest {package}/test" @@ -80,6 +64,9 @@ repair-wheel-command = [ "pipx run abi3audit --strict --report {wheel}", ] [tool.cibuildwheel.windows] +# Meson picks MinGW gcc by default on GitHub Actions Windows; --vsenv makes it +# set up the Visual Studio (MSVC) environment instead. +config-settings = { "setup-args" = "--vsenv" } repair-wheel-command = [ "copy {wheel} {dest_dir}", "pipx run abi3audit --strict --report {wheel}", From 8bb5846b3bbae3f2b24220347d76a8efd7c6b519 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 07:29:30 -0500 Subject: [PATCH 55/69] Fix Linux and Windows builds of the Meson migration error.c needs _XOPEN_SOURCE >= 700 or _GNU_SOURCE for strerror_l, so use _GNU_SOURCE on non-Apple POSIX hosts. Windows extraction broke because Git-bash tar reads the colon in a drive-letter path as a remote host; extract with Python tarfile instead. --- .github/actions/setup-mongo-c-driver/action.yml | 17 +++++++++++++++-- meson.build | 12 ++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/.github/actions/setup-mongo-c-driver/action.yml b/.github/actions/setup-mongo-c-driver/action.yml index 38deea8..41f7bdb 100644 --- a/.github/actions/setup-mongo-c-driver/action.yml +++ b/.github/actions/setup-mongo-c-driver/action.yml @@ -41,9 +41,22 @@ runs: test -s "$TARBALL" \ || { echo "::error::mongo-c-driver tarball is missing; cannot build" >&2; exit 1; } # Meson needs the extracted tree; extract if not already present (the - # extracted dir is also cached, so later runs skip this). + # extracted dir is also cached, so later runs skip this). Use Python + # tarfile: shell tar misreads the colon in Windows drive-letter paths + # (Git bash) as a remote host, and Python handles all three OSes. if [ ! -d "$SRCDIR" ]; then - tar -xzf "$TARBALL" -C "$GITHUB_WORKSPACE/.mongo-c-driver" + python - "$TARBALL" "$GITHUB_WORKSPACE/.mongo-c-driver" <<'PY' +import sys, tarfile +with tarfile.open(sys.argv[1]) as t: + for member in t.getmembers(): + try: + t.extract(member, sys.argv[2]) + except OSError: + # Windows needs elevated privileges to create symlinks; the build + # compiles explicit source files, so symlinks can be skipped. + if not (member.islnk() or member.issym()): + raise +PY fi test -f "$SRCDIR/src/libbson/src/bson/config.h.in" \ || { echo "::error::mongo-c-driver extraction failed" >&2; exit 1; } diff --git a/meson.build b/meson.build index fcc019f..3f317a0 100644 --- a/meson.build +++ b/meson.build @@ -10,11 +10,15 @@ fs = import('fs') cc = meson.get_compiler('c') threads = dependency('threads') -# libbson's bundled mlib uses POSIX clock/thread primitives that get selected -# via _POSIX_C_SOURCE; without it the build fails on POSIX hosts. Windows has -# its own code path (guarded on _WIN32), so leave it out there. -if host_machine.system() != 'windows' +# libbson's bundled mlib selects its POSIX clock/thread primitives from +# _POSIX_C_SOURCE (or _DEFAULT_SOURCE, which _GNU_SOURCE implies) and error.c +# needs _XOPEN_SOURCE >= 700 or _GNU_SOURCE for strerror_l/strerror_r. Define +# per-OS so the right feature-test macros are visible. Windows has its own code +# path (guarded on _WIN32), so leave it out entirely there. +if host_machine.system() == 'darwin' add_project_arguments('-D_POSIX_C_SOURCE=200809L', language: 'c') +elif host_machine.system() != 'windows' + add_project_arguments('-D_GNU_SOURCE', language: 'c') endif # --- libbson from source ------------------------------------------------ From 421bdfb37d0ed9024cf96e12c2184cc037b20f69 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 07:38:29 -0500 Subject: [PATCH 56/69] Fix action.yml YAML: extract mongo-c-driver with a one-line python call --- .github/actions/setup-mongo-c-driver/action.yml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/.github/actions/setup-mongo-c-driver/action.yml b/.github/actions/setup-mongo-c-driver/action.yml index 41f7bdb..c6fe1b6 100644 --- a/.github/actions/setup-mongo-c-driver/action.yml +++ b/.github/actions/setup-mongo-c-driver/action.yml @@ -45,18 +45,7 @@ runs: # tarfile: shell tar misreads the colon in Windows drive-letter paths # (Git bash) as a remote host, and Python handles all three OSes. if [ ! -d "$SRCDIR" ]; then - python - "$TARBALL" "$GITHUB_WORKSPACE/.mongo-c-driver" <<'PY' -import sys, tarfile -with tarfile.open(sys.argv[1]) as t: - for member in t.getmembers(): - try: - t.extract(member, sys.argv[2]) - except OSError: - # Windows needs elevated privileges to create symlinks; the build - # compiles explicit source files, so symlinks can be skipped. - if not (member.islnk() or member.issym()): - raise -PY + python -c "import tarfile,sys;tarfile.open(sys.argv[1]).extractall(sys.argv[2])" -- "$TARBALL" "$GITHUB_WORKSPACE/.mongo-c-driver" fi test -f "$SRCDIR/src/libbson/src/bson/config.h.in" \ || { echo "::error::mongo-c-driver extraction failed" >&2; exit 1; } From b060c0f642c0d822a019fcabae182dd7ee84e36b Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 07:45:20 -0500 Subject: [PATCH 57/69] extract mongo-c-driver without python -c '--' (sys.argv[1] became '--') --- .github/actions/setup-mongo-c-driver/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-mongo-c-driver/action.yml b/.github/actions/setup-mongo-c-driver/action.yml index c6fe1b6..2d9892f 100644 --- a/.github/actions/setup-mongo-c-driver/action.yml +++ b/.github/actions/setup-mongo-c-driver/action.yml @@ -45,7 +45,7 @@ runs: # tarfile: shell tar misreads the colon in Windows drive-letter paths # (Git bash) as a remote host, and Python handles all three OSes. if [ ! -d "$SRCDIR" ]; then - python -c "import tarfile,sys;tarfile.open(sys.argv[1]).extractall(sys.argv[2])" -- "$TARBALL" "$GITHUB_WORKSPACE/.mongo-c-driver" + python -c "import tarfile,sys;tarfile.open(sys.argv[1]).extractall(sys.argv[2])" "$TARBALL" "$GITHUB_WORKSPACE/.mongo-c-driver" fi test -f "$SRCDIR/src/libbson/src/bson/config.h.in" \ || { echo "::error::mongo-c-driver extraction failed" >&2; exit 1; } From cf586401415b6e81a5475906b57f74f060f9a605 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 07:53:15 -0500 Subject: [PATCH 58/69] Build Windows wheels on the windows-2022 runner (VS 2022/MSVC 17.x) Meson 1.12 cannot parse the /showIncludes dependency prefix that the windows-latest runner's new MSVC 19.51 (VS 2026) emits, so dep-prefix detection aborts. windows-2022 provides VS 2022 (MSVC 17.x), which Meson handles. --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index deb7bf1..d702f48 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -79,7 +79,7 @@ jobs: path: ./dist/*.whl build-windows: - runs-on: windows-latest + runs-on: windows-2022 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -163,7 +163,7 @@ jobs: test-windows: needs: build-windows - runs-on: windows-latest + runs-on: windows-2022 strategy: fail-fast: false matrix: From 59a71bdc6619bec7ec6d4d141d967375a3faa6da Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 08:02:20 -0500 Subject: [PATCH 59/69] Skip ccache setup on the Windows build job windows-2022 has no ccache preinstalled, so the setup-ccache action's ccache --zero-stats step exits 127. ccache is a GNU/Clang launcher and is not used by the MSVC/Meson Windows build, so don't set it up there. --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d702f48..92bd522 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -89,6 +89,7 @@ jobs: with: python-version: "3.11" - name: Set up ccache + if: runner.os != 'Windows' uses: ./.github/actions/setup-ccache with: key: ccache-${{ runner.os }}-wheel-${{ github.sha }} From f5c3698ce4b6b4fe7894a7ed33a7f696d8bfba59 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 08:06:27 -0500 Subject: [PATCH 60/69] Link ws2_32 as an import library on Windows libbson's bson-context.c calls gethostname, which needs ws2_32.lib on Windows. A bare 'ws2_32' link arg made the MSVC linker look for ws2_32.obj and fail with LNK1181; find_library resolves it correctly. --- meson.build | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 3f317a0..c19bd5e 100644 --- a/meson.build +++ b/meson.build @@ -138,6 +138,10 @@ libbson = static_library('bson', ) # --- Python extension module (Limited API / abi3) ------------------------ +# libbson's bson-context.c calls gethostname, which lives in ws2_32.lib on +# Windows; find_library resolves it to the import library (a bare 'ws2_32' +# link arg would be treated as an .obj and fail with LNK1181). +ws2_32 = cc.find_library('ws2_32', required: false) # bsonjs.__version__ comes from this macro; keep it in sync with the pyproject # version (meson.project_version() does not see meson-python's override). bsonjs_version = run_command(py, '-c', @@ -149,7 +153,7 @@ py.extension_module( 'bsonjs/bsonjs.c', c_args: ['-DBSON_STATIC', '-DBSONJS_VERSION="@0@"'.format(bsonjs_version)] + inc_flags, link_with: libbson, - link_args: host_machine.system() == 'windows' ? ['ws2_32'] : [], + dependencies: ws2_32.found() ? [ws2_32] : [], install: true, limited_api: '3.11', ) From 84c3663588e18b834100feaa9282aef878481d67 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 08:59:57 -0500 Subject: [PATCH 61/69] Drop dead CMake build; point version tooling at meson.build The Meson migration replaced CMakeLists.txt, so build docs and the libbson bump helper now reference meson.build. Removes the leftover BSONJS_MCD_TARBALL env var that Meson no longer reads. --- .../actions/setup-mongo-c-driver/action.yml | 1 - CMakeLists.txt | 138 ------------------ README.rst | 2 +- meson.build | 8 +- scripts/bump-libbson.sh | 2 +- scripts/bump_libbson.py | 36 +++-- 6 files changed, 30 insertions(+), 157 deletions(-) delete mode 100644 CMakeLists.txt diff --git a/.github/actions/setup-mongo-c-driver/action.yml b/.github/actions/setup-mongo-c-driver/action.yml index 2d9892f..5e3befd 100644 --- a/.github/actions/setup-mongo-c-driver/action.yml +++ b/.github/actions/setup-mongo-c-driver/action.yml @@ -49,4 +49,3 @@ runs: fi test -f "$SRCDIR/src/libbson/src/bson/config.h.in" \ || { echo "::error::mongo-c-driver extraction failed" >&2; exit 1; } - echo "BSONJS_MCD_TARBALL=$TARBALL" >> "$GITHUB_ENV" diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 855a1dd..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,138 +0,0 @@ -cmake_minimum_required(VERSION 3.26) - -# Wrap the compiler in ccache when it's available so the repeated libbson -# build in CI is served from the cache. Must be set before project() so the -# launcher applies to the C compiler (and the FetchContent sub-build). Only -# enable it on GNU/Clang platforms: ccache's MSVC launcher hangs CMake's -# compiler detection on Windows. -if(NOT WIN32) - find_program(CCACHE_PROGRAM ccache) - if(CCACHE_PROGRAM) - set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") - endif() -endif() - -project(bsonjs LANGUAGES C) - -find_package(Python COMPONENTS Interpreter Development.Module Development.SABIModule REQUIRED) - -# Read the package version from pyproject.toml for BSONJS_VERSION. -file(READ "${CMAKE_CURRENT_SOURCE_DIR}/pyproject.toml" _pyproject) -string(REGEX MATCH "version = \"([^\"]*)\"" _ "${_pyproject}") -set(_package_version "${CMAKE_MATCH_1}") - -# --- libbson static library via FetchContent --- -include(FetchContent) - -# Small timing helper: logs a phase marker with an elapsed counter so a -# completed or cancelled CI log shows where the build time is going. -string(TIMESTAMP _bsonjs_last "%s") -function(bsonjs_mark msg) - string(TIMESTAMP _now "%s") - math(EXPR _elapsed "${_now} - ${_bsonjs_last}") - message(STATUS "[bsonjs] ${msg} (+${_elapsed}s)") - set(_bsonjs_last "${_now}" PARENT_SCOPE) -endfunction() -bsonjs_mark("configure begin") - -# The mongo-c-driver release tarball is pre-fetched by CI -# (.github/actions/setup-mongo-c-driver) and passed in via BSONJS_MCD_TARBALL, -# so the CI build phase is download-free. Otherwise (dist.yml wheels, sdist, -# local builds) download with hard timeouts so a stalled connection fails fast -# instead of hanging the build for tens of minutes. -set(_mcd_url "https://github.com/mongodb/mongo-c-driver/archive/refs/tags/2.5.3.tar.gz") -set(_mcd_archive "${CMAKE_CURRENT_BINARY_DIR}/mongo-c-driver-2.5.3.tar.gz") -if(DEFINED ENV{BSONJS_MCD_TARBALL}) - # Normalize Windows backslash paths, which would otherwise break the - # FetchContent subbuild's generated CMakeLists.txt. - file(TO_CMAKE_PATH "$ENV{BSONJS_MCD_TARBALL}" _mcd_cached) - if(EXISTS "${_mcd_cached}") - set(_mcd_archive "${_mcd_cached}") - endif() -endif() -message(STATUS "[bsonjs] mongo-c-driver BSONJS_MCD_TARBALL=$ENV{BSONJS_MCD_TARBALL}") -message(STATUS "[bsonjs] mongo-c-driver archive=${_mcd_archive}") -if(NOT EXISTS "${_mcd_archive}") - bsonjs_mark("archive missing -> downloading") - file(DOWNLOAD - "${_mcd_url}" - "${_mcd_archive}" - TLS_VERIFY ON - TIMEOUT 300 - INACTIVITY_TIMEOUT 120 - RETRY_COUNT 3 - RETRY_DELAY 5 - STATUS _mcd_status - ) - list(GET _mcd_status 0 _mcd_code) - if(NOT _mcd_code EQUAL 0) - message(FATAL_ERROR "Failed to download mongo-c-driver: ${_mcd_status}") - endif() - bsonjs_mark("archive downloaded") -else() - file(SIZE "${_mcd_archive}" _mcd_size) - message(STATUS "[bsonjs] mongo-c-driver archive size=${_mcd_size} bytes (cached)") -endif() - -FetchContent_Declare( - mongo-c-driver - URL "${_mcd_archive}" - DOWNLOAD_EXTRACT_TIMESTAMP TRUE -) - -# We only need libbson, not libmongoc, which requires a TLS/crypto library. -# These must be set BEFORE FetchContent_MakeAvailable so they are honored when -# the subproject configures; otherwise libmongoc (and its zlib/utf8proc/SASL/ -# shared-variant dependencies) get built, which is slow and fails without TLS. -set(ENABLE_MONGOC OFF) -set(ENABLE_SHARED OFF) -set(ENABLE_STATIC ON) -# The driver builds a C++ test gated on BUILD_TESTING; keep the build C-only. -set(BUILD_TESTING OFF) -set(ENABLE_TESTS OFF) - -bsonjs_mark("FetchContent_MakeAvailable begin") -FetchContent_MakeAvailable(mongo-c-driver) -bsonjs_mark("FetchContent_MakeAvailable done") - -# bson:static (alias bson_static) propagates BSON_STATIC already, but the -# driver does not export its include dirs on the target, so add them here so -# both #include and #include resolve. The generated -# config.h/version.h live in the binary dir, and the common/mlib headers come -# from the common tree. -set(_bson_src "${mongo-c-driver_SOURCE_DIR}/src/libbson") -set(_bson_bin "${mongo-c-driver_BINARY_DIR}/src/libbson") -set(_common_src "${mongo-c-driver_SOURCE_DIR}/src/common") -set(_common_bin "${mongo-c-driver_BINARY_DIR}/src/common") - -# --- Build the extension module --- -# USE_SABI links Python::SABIModule and sets Py_LIMITED_API from the version, -# producing a module suffix that stays importable across every CPython >= 3.11, -# so a single cp311-abi3 wheel is reused for all of them. (WITH_SOABI would bake -# the build interpreter's SOABI into the name, which newer interpreters can't -# discover.) -Python_add_library(bsonjs MODULE USE_SABI 3.11 "${CMAKE_CURRENT_SOURCE_DIR}/bsonjs/bsonjs.c") - -target_include_directories(bsonjs PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/bsonjs" - "${_bson_src}/src" - "${_bson_src}/src/bson" - "${_bson_bin}/src" - "${_common_src}/src" - "${_common_bin}/src" -) - -target_compile_definitions(bsonjs PRIVATE - BSONJS_VERSION="${_package_version}" - BSON_STATIC -) - -if(WIN32) - target_link_libraries(bsonjs PRIVATE bson_static ws2_32) -elseif(APPLE) - target_link_libraries(bsonjs PRIVATE bson_static) -else() - target_link_libraries(bsonjs PRIVATE bson_static rt) -endif() - -install(TARGETS bsonjs DESTINATION .) diff --git a/README.rst b/README.rst index ea9c1f9..9a7d79a 100644 --- a/README.rst +++ b/README.rst @@ -145,7 +145,7 @@ Updating libbson ```````````````` The package pulls libbson from the mongo-c-driver release pinned in -`CMakeLists.txt`. To bump the version, rebuild, and refresh the benchmark +`meson.build`. To bump the version, rebuild, and refresh the benchmark numbers in the Speed section, run:: $ bash scripts/bump-libbson.sh diff --git a/meson.build b/meson.build index c19bd5e..7238241 100644 --- a/meson.build +++ b/meson.build @@ -22,12 +22,12 @@ elif host_machine.system() != 'windows' endif # --- libbson from source ------------------------------------------------ -# Meson has no FetchContent, so the workflow pre-fetches and extracts the -# mongo-c-driver release tarball and points BSONJS_MCD_SRC at it. We compile -# only libbson (static) — never libmongoc — by listing its sources here. +# Meson has no FetchContent, so the workflow extracts the mongo-c-driver +# release tarball to .mongo-c-driver/ and we compile only libbson (static) +# here, never libmongoc, by listing its sources explicitly. mcd_src = get_option('mongo-c-driver-dir') if mcd_src == '' - # Fallback: the workflow pre-fetches + extracts the release tarball here. + # Fallback: the workflow pre-fetches and extracts the release tarball here. fallback = meson.project_source_root() / '.mongo-c-driver' / 'mongo-c-driver-2.5.3' if fs.is_dir(fallback) mcd_src = fallback diff --git a/scripts/bump-libbson.sh b/scripts/bump-libbson.sh index 4cfcd1b..88c18f3 100755 --- a/scripts/bump-libbson.sh +++ b/scripts/bump-libbson.sh @@ -26,7 +26,7 @@ else LIBBSON_VERSION="$1" fi -# 1. Update the libbson version in CMakeLists.txt, the README About line, +# 1. Update the libbson version in meson.build, the README About line, # and the CHANGELOG 0.8.0 entry. python3 "${BUMP_PY}" update-versions "$LIBBSON_VERSION" echo "Updated libbson version to: ${LIBBSON_VERSION}" diff --git a/scripts/bump_libbson.py b/scripts/bump_libbson.py index ad7fe5f..b5b76eb 100644 --- a/scripts/bump_libbson.py +++ b/scripts/bump_libbson.py @@ -10,9 +10,8 @@ REPO_ROOT = Path(__file__).resolve().parent.parent README = REPO_ROOT / "README.rst" CHANGELOG = REPO_ROOT / "CHANGELOG.rst" -CMAKE_LISTS = REPO_ROOT / "CMakeLists.txt" +MESON_BUILD = REPO_ROOT / "meson.build" -TAG_URL = r"refs/tags/[0-9]+\.[0-9]+\.[0-9]+\.tar\.gz" LATEST_RELEASE_URL = ( "https://api.github.com/repos/mongodb/mongo-c-driver/releases/latest" ) @@ -29,12 +28,12 @@ def package_version(): def current_version(): - """Return the libbson version pinned in CMakeLists.txt.""" - text = CMAKE_LISTS.read_text() - m = re.search(TAG_URL, text) + """Return the libbson version pinned in meson.build.""" + text = MESON_BUILD.read_text() + m = re.search(r"mongo-c-driver-([0-9]+\.[0-9]+\.[0-9]+)", text) if not m: - raise SystemExit("Could not read the libbson version from CMakeLists.txt") - return re.search(r"([0-9]+\.[0-9]+\.[0-9]+)", m.group(0)).group(1) + raise SystemExit("Could not read the libbson version from meson.build") + return m.group(1) def latest_version(): @@ -59,12 +58,25 @@ def sub_file(path, pattern, repl, label): def update_versions(version): """Update the pinned libbson version in the build files and docs.""" - sub_file( - CMAKE_LISTS, - TAG_URL, - "refs/tags/{}.tar.gz".format(version), - "FetchContent URL", + # Pin the libbson version in meson.build: the source-dir paths (fallback + # and error message), the generated version header, and the three + # libbson_major/minor/patch macros. + major, minor, patch = version.split(".") + text = MESON_BUILD.read_text() + text = re.sub( + r"mongo-c-driver-[0-9]+\.[0-9]+\.[0-9]+", + "mongo-c-driver-{}".format(version), + text, + ) + text = re.sub( + r"libbson_VERSION_FULL',\s*'[0-9]+\.[0-9]+\.[0-9]+'", + "libbson_VERSION_FULL', '{}'".format(version), + text, ) + text = re.sub(r"libbson_major = [0-9]+", "libbson_major = {}".format(major), text) + text = re.sub(r"libbson_minor = [0-9]+", "libbson_minor = {}".format(minor), text) + text = re.sub(r"libbson_patch = [0-9]+", "libbson_patch = {}".format(patch), text) + MESON_BUILD.write_text(text) # README About link: http://mongoc.org/libbson// sub_file( README, From 1c90473a6d2a9aa6769d627321eef04a96b6c668 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 09:44:53 -0500 Subject: [PATCH 62/69] Address code review: bootstrap the sdist and wire CodeQL to Meson Add scripts/fetch_mongo_c_driver.py, shared by the meson build, the CI setup action, and CodeQL. It reads the version and SHA256 pin from meson.build, so bump_libbson.py updates both and no caller hardcodes a version. meson.build fetches the pinned release when .mongo-c-driver/ is absent, so an sdist can build itself. Correct the source-build wording, require meson 1.3 for limited_api, and fix the stale CMake references. --- .github/actions/setup-ccache/action.yml | 2 +- .../actions/setup-mongo-c-driver/action.yml | 45 ++----- .github/workflows/codeql.yml | 25 ++-- CHANGELOG.rst | 9 +- README.rst | 9 +- meson.build | 31 ++++- pyproject.toml | 2 +- scripts/bump_libbson.py | 29 ++++ scripts/fetch_mongo_c_driver.py | 125 ++++++++++++++++++ 9 files changed, 214 insertions(+), 63 deletions(-) create mode 100644 scripts/fetch_mongo_c_driver.py diff --git a/.github/actions/setup-ccache/action.yml b/.github/actions/setup-ccache/action.yml index e72e274..5d43cd6 100644 --- a/.github/actions/setup-ccache/action.yml +++ b/.github/actions/setup-ccache/action.yml @@ -37,7 +37,7 @@ runs: shell: bash run: | # Only used as a compiler launcher on GNU/Clang; ccache's MSVC launcher - # hangs CMake's compiler detection on Windows, so skip it there. + # hangs compiler detection on Windows, so skip it there. if [ "$RUNNER_OS" = "Linux" ]; then sudo apt-get install -y ccache elif [ "$RUNNER_OS" = "macOS" ]; then diff --git a/.github/actions/setup-mongo-c-driver/action.yml b/.github/actions/setup-mongo-c-driver/action.yml index 5e3befd..0cfc411 100644 --- a/.github/actions/setup-mongo-c-driver/action.yml +++ b/.github/actions/setup-mongo-c-driver/action.yml @@ -1,5 +1,5 @@ name: Setup mongo-c-driver -description: "Cache and fetch the mongo-c-driver release tarball used by FetchContent" +description: "Cache and fetch the mongo-c-driver release pinned in meson.build" runs: using: "composite" steps: @@ -12,40 +12,21 @@ runs: Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE" -ErrorAction SilentlyContinue Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\build" -ErrorAction SilentlyContinue + - name: Read the pinned mongo-c-driver version + id: pinned + shell: bash + run: | + set -euo pipefail + VERSION=$(python -c "import re,pathlib; print(re.search(r'mongo-c-driver-([0-9.]+)', pathlib.Path('meson.build').read_text()).group(1))") + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + - name: Restore/save mongo-c-driver tarball uses: actions/cache@v4 with: path: ${{ github.workspace }}/.mongo-c-driver - key: mcd-2.5.3-${{ runner.os }}-${{ github.sha }} - restore-keys: mcd-2.5.3-${{ runner.os }}- + key: mcd-${{ steps.pinned.outputs.version }}-${{ runner.os }}-${{ github.sha }} + restore-keys: mcd-${{ steps.pinned.outputs.version }}-${{ runner.os }}- - - name: Ensure mongo-c-driver tarball is present and extracted + - name: Fetch and extract mongo-c-driver shell: bash - run: | - set -euo pipefail - mkdir -p "$GITHUB_WORKSPACE/.mongo-c-driver" - TARBALL="$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3.tar.gz" - SRCDIR="$GITHUB_WORKSPACE/.mongo-c-driver/mongo-c-driver-2.5.3" - if [ -s "$TARBALL" ]; then - echo "[bsonjs] mongo-c-driver tarball restored from cache ($(stat -c%s "$TARBALL" 2>/dev/null || stat -f%z "$TARBALL") bytes)" - else - echo "[bsonjs] mongo-c-driver tarball not cached; downloading from codeload..." - curl -fsSL \ - --retry 3 \ - --connect-timeout 30 \ - --max-time 180 \ - -o "$TARBALL" \ - https://github.com/mongodb/mongo-c-driver/archive/refs/tags/2.5.3.tar.gz - echo "[bsonjs] mongo-c-driver tarball downloaded ($(stat -c%s "$TARBALL" 2>/dev/null || stat -f%z "$TARBALL") bytes)" - fi - test -s "$TARBALL" \ - || { echo "::error::mongo-c-driver tarball is missing; cannot build" >&2; exit 1; } - # Meson needs the extracted tree; extract if not already present (the - # extracted dir is also cached, so later runs skip this). Use Python - # tarfile: shell tar misreads the colon in Windows drive-letter paths - # (Git bash) as a remote host, and Python handles all three OSes. - if [ ! -d "$SRCDIR" ]; then - python -c "import tarfile,sys;tarfile.open(sys.argv[1]).extractall(sys.argv[2])" "$TARBALL" "$GITHUB_WORKSPACE/.mongo-c-driver" - fi - test -f "$SRCDIR/src/libbson/src/bson/config.h.in" \ - || { echo "::error::mongo-c-driver extraction failed" >&2; exit 1; } + run: python scripts/fetch_mongo_c_driver.py diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index f3eacac..4cd0093 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -26,26 +26,19 @@ jobs: include: - language: c-cpp build-mode: manual - # Configure the project so CMake fetches mongo-c-driver and - # generates libbson's config.h/version.h. Then compile only our own - # bsonjs.c against that tree. CodeQL's tracer only extracts files the - # compiler is actually invoked on, so keeping libbson itself out of - # scope means never compiling it here. + # Configure with Meson so libbson's config.h/version.h are generated. + # Then compile only our own bsonjs.c against that tree. CodeQL's + # tracer only extracts files the compiler is actually invoked on, so + # keeping libbson itself out of scope means never compiling it here. manual-build-command: | - if ! command -v cmake >/dev/null 2>&1; then pip install "cmake>=3.17,<4"; fi - cmake -S . -B /tmp/codeql-build \ - -DENABLE_MONGOC=OFF -DENABLE_SHARED=OFF -DENABLE_STATIC=ON \ - -DENABLE_TESTS=OFF -DBUILD_TESTING=OFF \ - -DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF \ - -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ - -DCMAKE_BUILD_TYPE=Release + pip install "meson>=1.3" ninja + MCD=$(python3 scripts/fetch_mongo_c_driver.py --print-srcdir) + meson setup /tmp/codeql-build PYINC=$(python3 -c "import sysconfig; print(sysconfig.get_paths()['include'])") BSONJS_VERSION=$(python3 -c "import re; print(re.search(r'(?m)^version\s*=\s*\"([^\"]+)\"', open('pyproject.toml').read()).group(1))") - SRC=/tmp/codeql-build/_deps/mongo-c-driver-src - BIN=/tmp/codeql-build/_deps/mongo-c-driver-build gcc -c bsonjs/bsonjs.c -o /tmp/bsonjs.o \ - -Ibsonjs -I"$SRC/src/libbson/src" -I"$SRC/src/libbson/src/bson" \ - -I"$BIN/src/libbson/src" -I"$SRC/src/common/src" -I"$BIN/src/common/src" \ + -Ibsonjs -I"$MCD/src/libbson/src" -I"$MCD/src/libbson/src/bson" \ + -I/tmp/codeql-build -I"$MCD/src/common/src" \ -I"$PYINC" \ -DBSON_STATIC -DPy_LIMITED_API=0x030b0000 "-DBSONJS_VERSION=\"$BSONJS_VERSION\"" - language: python diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2ec24dd..bad264f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,10 +3,11 @@ Changelog 0.8.0 ````` -Version 0.8.0 builds libbson 2.5.3 from source at install time instead of -vendoring C sources. Building from source requires CMake 3.26+ and Python -3.11+ (up from 3.9), so the extension is built against the CPython 3.11 -Limited API. +Version 0.8.0 updates the bundled libbson to 2.5.3 and compiles it from the +pinned mongo-c-driver release instead of vendoring C sources. The build now +uses Meson and requires Python 3.11+ (up from 3.9), so the extension is +built against the CPython 3.11 Limited API. Building from source downloads +the mongo-c-driver release tarball on first build. For a detailed breakdown of what changed in each version of libbson see its changelog: https://github.com/mongodb/mongo-c-driver/blob/2.5.3/NEWS http://mongoc.org/libbson/2.5.3/ diff --git a/README.rst b/README.rst index 9a7d79a..f1e7583 100644 --- a/README.rst +++ b/README.rst @@ -107,9 +107,12 @@ like so Installing From Source ====================== -python-bsonjs supports CPython 3.11+. Building from source downloads and -builds libbson with CMake, so you need CMake 3.26+ and an internet -connection at install time. +python-bsonjs supports CPython 3.11+ and builds with Meson through +meson-python. The build compiles libbson from the mongo-c-driver release +pinned in ``meson.build``. It downloads that release on first build, so +the first build needs an internet connection. To build offline, extract +the release under ``.mongo-c-driver/`` or pass +``-Dmongo-c-driver-dir=/path/to/mongo-c-driver-`` to meson. Compiler ```````` diff --git a/meson.build b/meson.build index 7238241..db59659 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,8 @@ project( 'bsonjs', 'c', - version: '0.8.0', + version: '0.8.0.dev0', + meson_version: '>=1.3', default_options: ['c_std=c99', 'warning_level=1'], ) @@ -22,13 +23,31 @@ elif host_machine.system() != 'windows' endif # --- libbson from source ------------------------------------------------ -# Meson has no FetchContent, so the workflow extracts the mongo-c-driver -# release tarball to .mongo-c-driver/ and we compile only libbson (static) -# here, never libmongoc, by listing its sources explicitly. +# Meson has no FetchContent, so the mongo-c-driver release tarball is +# extracted to .mongo-c-driver/: the CI action pre-fetches it, and meson +# fetches it here when absent. We compile only libbson (static), never +# libmongoc, by listing its sources explicitly. +# +# SHA256 of that tarball, read by scripts/fetch_mongo_c_driver.py and updated +# by scripts/bump_libbson.py alongside the version. +mcd_sha256 = '5eb6f2297f5fbfcf0bf79942623b84f883e9223f008f24497ae60937802a6315' + mcd_src = get_option('mongo-c-driver-dir') if mcd_src == '' - # Fallback: the workflow pre-fetches and extracts the release tarball here. + # Fallback: the CI action pre-fetches and extracts the release tarball here. fallback = meson.project_source_root() / '.mongo-c-driver' / 'mongo-c-driver-2.5.3' + if not fs.is_dir(fallback) + # Nothing pre-fetched, so fetch the pinned release now. This lets an + # sdist install build itself without a separate setup step. + fetch = run_command( + py, + meson.project_source_root() / 'scripts' / 'fetch_mongo_c_driver.py', + check: false, + ) + if fetch.returncode() != 0 + error('Failed to fetch mongo-c-driver:\n' + fetch.stdout() + fetch.stderr()) + endif + endif if fs.is_dir(fallback) mcd_src = fallback endif @@ -146,7 +165,7 @@ ws2_32 = cc.find_library('ws2_32', required: false) # version (meson.project_version() does not see meson-python's override). bsonjs_version = run_command(py, '-c', 'import re,pathlib;print(re.search(r\'version\\s*=\\s*"([^"]+)"\',pathlib.Path("pyproject.toml").read_text()).group(1))', - check: false).stdout().strip() + check: true).stdout().strip() py.extension_module( 'bsonjs', diff --git a/pyproject.toml b/pyproject.toml index 42bde3d..c9db524 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["meson-python>=0.17", "meson>=1.2", "ninja"] +requires = ["meson-python>=0.17", "meson>=1.3", "ninja"] build-backend = "mesonpy" [project] diff --git a/scripts/bump_libbson.py b/scripts/bump_libbson.py index b5b76eb..af45bc0 100644 --- a/scripts/bump_libbson.py +++ b/scripts/bump_libbson.py @@ -2,6 +2,7 @@ """Version bump helper used by bump-libbson.sh.""" import argparse +import hashlib import json import re import urllib.request @@ -15,6 +16,10 @@ LATEST_RELEASE_URL = ( "https://api.github.com/repos/mongodb/mongo-c-driver/releases/latest" ) +RELEASE_URL = ( + "https://github.com/mongodb/mongo-c-driver/releases/download/" + "{version}/mongo-c-driver-{version}.tar.gz" +) def package_version(): @@ -48,6 +53,23 @@ def latest_version(): return data["tag_name"].lstrip("v") +def release_sha256(version): + """Return the SHA256 of the mongo-c-driver release tarball.""" + url = RELEASE_URL.format(version=version) + digest = hashlib.sha256() + request = urllib.request.Request(url, headers={"User-Agent": "python-bsonjs"}) + try: + with urllib.request.urlopen(request, timeout=180) as resp: + while True: + chunk = resp.read(1024 * 1024) + if not chunk: + break + digest.update(chunk) + except Exception as exc: + raise SystemExit("Could not download {}: {}".format(url, exc)) + return digest.hexdigest() + + def sub_file(path, pattern, repl, label): """Replace the first match of pattern in path with repl.format(version).""" text = path.read_text() @@ -76,6 +98,13 @@ def update_versions(version): text = re.sub(r"libbson_major = [0-9]+", "libbson_major = {}".format(major), text) text = re.sub(r"libbson_minor = [0-9]+", "libbson_minor = {}".format(minor), text) text = re.sub(r"libbson_patch = [0-9]+", "libbson_patch = {}".format(patch), text) + if not re.search(r"mcd_sha256\s*=\s*'[0-9a-f]{64}'", text): + raise SystemExit("Could not find mcd_sha256 in meson.build") + text = re.sub( + r"mcd_sha256\s*=\s*'[0-9a-f]{64}'", + "mcd_sha256 = '{}'".format(release_sha256(version)), + text, + ) MESON_BUILD.write_text(text) # README About link: http://mongoc.org/libbson// sub_file( diff --git a/scripts/fetch_mongo_c_driver.py b/scripts/fetch_mongo_c_driver.py new file mode 100644 index 0000000..19e53d3 --- /dev/null +++ b/scripts/fetch_mongo_c_driver.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +"""Fetch and extract the mongo-c-driver release pinned in meson.build.""" + +import argparse +import hashlib +import re +import sys +import tarfile +import urllib.request +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +MESON_BUILD = REPO_ROOT / "meson.build" +DEFAULT_DEST = REPO_ROOT / ".mongo-c-driver" +RELEASE_URL = ( + "https://github.com/mongodb/mongo-c-driver/releases/download/" + "{version}/mongo-c-driver-{version}.tar.gz" +) +CONFIG_H = "src/libbson/src/bson/config.h.in" + + +def _log(message): + print(message, file=sys.stderr) + + +def _meson_value(pattern, description): + match = re.search(pattern, MESON_BUILD.read_text(), re.MULTILINE) + if not match: + raise SystemExit("Could not read {} from meson.build".format(description)) + return match.group(1) + + +def pinned_version(): + """Return the mongo-c-driver version pinned in meson.build.""" + return _meson_value( + r"mongo-c-driver-([0-9]+\.[0-9]+\.[0-9]+)", "the mongo-c-driver version" + ) + + +def pinned_sha256(): + """Return the tarball SHA256 pinned in meson.build.""" + return _meson_value(r"mcd_sha256\s*=\s*'([0-9a-f]{64})'", "mcd_sha256") + + +def sha256_of(path): + """Return the hex SHA256 digest of path.""" + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def download(url, dest): + """Download url to dest.""" + _log("[bsonjs] downloading {}".format(url)) + request = urllib.request.Request(url, headers={"User-Agent": "python-bsonjs"}) + with urllib.request.urlopen(request, timeout=180) as response, dest.open( + "wb" + ) as handle: + while True: + chunk = response.read(1024 * 1024) + if not chunk: + break + handle.write(chunk) + + +def extract(tarball, dest_root): + """Extract tarball into dest_root.""" + with tarfile.open(tarball) as archive: + if sys.version_info >= (3, 12): + archive.extractall(dest_root, filter="data") + else: + archive.extractall(dest_root) + + +def fetch(dest_root): + """Ensure the pinned mongo-c-driver release is extracted under dest_root. + + Returns the extracted source directory. Reuses an existing extraction. + """ + version = pinned_version() + expected = pinned_sha256() + srcdir = dest_root / "mongo-c-driver-{}".format(version) + if (srcdir / CONFIG_H).is_file(): + _log("[bsonjs] mongo-c-driver {} already extracted".format(version)) + return srcdir + dest_root.mkdir(parents=True, exist_ok=True) + tarball = dest_root / "mongo-c-driver-{}.tar.gz".format(version) + if tarball.is_file() and sha256_of(tarball) != expected: + _log("[bsonjs] cached tarball checksum mismatch; re-downloading") + tarball.unlink() + if not tarball.is_file(): + download(RELEASE_URL.format(version=version), tarball) + actual = sha256_of(tarball) + if actual != expected: + tarball.unlink() + raise SystemExit( + "SHA256 mismatch for mongo-c-driver {}: expected {}, got {}".format( + version, expected, actual + ) + ) + extract(tarball, dest_root) + if not (srcdir / CONFIG_H).is_file(): + raise SystemExit("Extraction of mongo-c-driver {} failed".format(version)) + _log("[bsonjs] extracted mongo-c-driver {} to {}".format(version, srcdir)) + return srcdir + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--dest", type=Path, default=DEFAULT_DEST) + parser.add_argument( + "--print-srcdir", + action="store_true", + help="print the extracted source directory to stdout", + ) + args = parser.parse_args() + srcdir = fetch(args.dest) + if args.print_srcdir: + print(srcdir) + + +if __name__ == "__main__": + main() From f8d0dca0ec913e6385a9fcb12ad2c4d7158c41ef Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 09:53:04 -0500 Subject: [PATCH 63/69] Build 64-bit Windows wheels only meson-python cannot find the 32-bit Python headers in the 32-bit cibuildwheel environment, so the win32 build fails. Restrict the Windows build to AMD64 and note the dropped architecture in the changelog. --- CHANGELOG.rst | 3 ++- pyproject.toml | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bad264f..d035ba4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,7 +7,8 @@ Version 0.8.0 updates the bundled libbson to 2.5.3 and compiles it from the pinned mongo-c-driver release instead of vendoring C sources. The build now uses Meson and requires Python 3.11+ (up from 3.9), so the extension is built against the CPython 3.11 Limited API. Building from source downloads -the mongo-c-driver release tarball on first build. +the mongo-c-driver release tarball on first build. Windows wheels are now +64-bit only. For a detailed breakdown of what changed in each version of libbson see its changelog: https://github.com/mongodb/mongo-c-driver/blob/2.5.3/NEWS http://mongoc.org/libbson/2.5.3/ diff --git a/pyproject.toml b/pyproject.toml index c9db524..52b4168 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,6 +67,9 @@ repair-wheel-command = [ # Meson picks MinGW gcc by default on GitHub Actions Windows; --vsenv makes it # set up the Visual Studio (MSVC) environment instead. config-settings = { "setup-args" = "--vsenv" } +# Build 64-bit wheels only: meson-python cannot find the 32-bit Python headers +# in the 32-bit cibuildwheel environment. +archs = ["AMD64"] repair-wheel-command = [ "copy {wheel} {dest_dir}", "pipx run abi3audit --strict --report {wheel}", From b0586a8669fc2be9ea776d7e941c2b1fc17706f5 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 10:35:22 -0500 Subject: [PATCH 64/69] Run the Python Dist Windows build on windows-2022 Meson cannot parse the /showIncludes dependency prefix that the windows-latest runner's new MSVC 19.51 (VS 2026) emits, so dependency detection aborts. windows-2022 provides VS 2022 (MSVC 17.x), which Meson handles. The test workflow already pins windows-2022 for the same reason. --- .github/workflows/dist.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 9d3b4e0..a6dee33 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -28,7 +28,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-latest, windows-latest, ubuntu-24.04-arm, ubuntu-latest] + os: [macos-latest, windows-2022, ubuntu-24.04-arm, ubuntu-latest] name: Build CPython-${{ matrix.os }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 From 73fa85c409c184680dbbe1f5bc5b9c6e77b4a965 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 10:43:01 -0500 Subject: [PATCH 65/69] Skip ccache setup on the dist Windows build windows-2022 has no ccache preinstalled, so the setup-ccache action's ccache --zero-stats step exits 127. ccache is a GNU/Clang launcher and is not used by the MSVC/Meson Windows build, so don't set it up there. The test workflow already guards this. --- .github/workflows/dist.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index a6dee33..06836be 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -35,6 +35,7 @@ jobs: with: persist-credentials: false - name: Set up ccache + if: runner.os != 'Windows' uses: ./.github/actions/setup-ccache with: key: ccache-${{ runner.os }}-${{ github.sha }} From fb2e8735c95fa37d50061e9566a14c7ead258a80 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 11:02:30 -0500 Subject: [PATCH 66/69] Define _DEFAULT_SOURCE for the Linux build musl does not alias _GNU_SOURCE to _DEFAULT_SOURCE, so libbson's mlib time_point.h fails its POSIX clock check and the musllinux aarch64 build aborts. glibc aliases the two, which is why manylinux passes. Also run the dist wheel matrix with fail-fast disabled so one failing platform does not cancel the others. --- .github/workflows/dist.yml | 1 + meson.build | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 06836be..244b882 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -27,6 +27,7 @@ jobs: build_wheels: runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: [macos-latest, windows-2022, ubuntu-24.04-arm, ubuntu-latest] name: Build CPython-${{ matrix.os }} diff --git a/meson.build b/meson.build index db59659..b2e03c0 100644 --- a/meson.build +++ b/meson.build @@ -11,15 +11,15 @@ fs = import('fs') cc = meson.get_compiler('c') threads = dependency('threads') -# libbson's bundled mlib selects its POSIX clock/thread primitives from -# _POSIX_C_SOURCE (or _DEFAULT_SOURCE, which _GNU_SOURCE implies) and error.c -# needs _XOPEN_SOURCE >= 700 or _GNU_SOURCE for strerror_l/strerror_r. Define -# per-OS so the right feature-test macros are visible. Windows has its own code -# path (guarded on _WIN32), so leave it out entirely there. +# libbson's bundled mlib selects its POSIX clock primitives from _POSIX_C_SOURCE +# or _DEFAULT_SOURCE, and error.c needs _XOPEN_SOURCE >= 700 or _GNU_SOURCE for +# strerror_l/strerror_r. glibc aliases _GNU_SOURCE to _DEFAULT_SOURCE but musl +# does not, so define both. Windows has its own code path (guarded on _WIN32), +# so leave it out entirely there. if host_machine.system() == 'darwin' add_project_arguments('-D_POSIX_C_SOURCE=200809L', language: 'c') elif host_machine.system() != 'windows' - add_project_arguments('-D_GNU_SOURCE', language: 'c') + add_project_arguments('-D_GNU_SOURCE', '-D_DEFAULT_SOURCE', language: 'c') endif # --- libbson from source ------------------------------------------------ From 174cd9aa71d542e3f05238a044e87e9ede0b5f1f Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 11:20:20 -0500 Subject: [PATCH 67/69] Define _XOPEN_SOURCE=700 for the Linux build libbson's error.c selects strerror_l only when _XOPEN_SOURCE >= 700. musl does not derive that from _GNU_SOURCE, so the musllinux build falls back to glibc-style strerror_r and fails to compile. glibc derives it, which is why manylinux passes. --- meson.build | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index b2e03c0..31c0a71 100644 --- a/meson.build +++ b/meson.build @@ -11,15 +11,20 @@ fs = import('fs') cc = meson.get_compiler('c') threads = dependency('threads') -# libbson's bundled mlib selects its POSIX clock primitives from _POSIX_C_SOURCE -# or _DEFAULT_SOURCE, and error.c needs _XOPEN_SOURCE >= 700 or _GNU_SOURCE for -# strerror_l/strerror_r. glibc aliases _GNU_SOURCE to _DEFAULT_SOURCE but musl -# does not, so define both. Windows has its own code path (guarded on _WIN32), -# so leave it out entirely there. +# libbson's mlib selects its POSIX clock primitives from _POSIX_C_SOURCE or +# _DEFAULT_SOURCE, and error.c uses strerror_l only when _XOPEN_SOURCE >= 700. +# glibc derives both from _GNU_SOURCE; musl derives neither, so define them +# explicitly. Windows has its own code path (guarded on _WIN32), so leave it out +# entirely there. if host_machine.system() == 'darwin' add_project_arguments('-D_POSIX_C_SOURCE=200809L', language: 'c') elif host_machine.system() != 'windows' - add_project_arguments('-D_GNU_SOURCE', '-D_DEFAULT_SOURCE', language: 'c') + add_project_arguments( + '-D_GNU_SOURCE', + '-D_DEFAULT_SOURCE', + '-D_XOPEN_SOURCE=700', + language: 'c', + ) endif # --- libbson from source ------------------------------------------------ From c6c85835e51261c4eab54fdec4ed30bdbb46dec6 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 15 Sep 2026 12:39:57 -0500 Subject: [PATCH 68/69] Restore the 32-bit Windows wheel build Meson's --vsenv activates only the x64 toolset, and its Python dependency check rejects a 32-bit Python against an x64 compiler ("Need python for x86_64, but found x86"), which is why the 64-bit-only workaround landed. The dist workflow now runs a second cibuildwheel pass for x86 with the x86 toolset exported first, so Meson skips its own activation and detects the host as x86. --- .github/actions/setup-ccache/action.yml | 2 +- .../actions/setup-mongo-c-driver/action.yml | 4 +- .github/workflows/codeql.yml | 22 +++-- .github/workflows/dist.yml | 40 +++++++-- .github/workflows/release-python.yml | 2 +- .github/workflows/test.yml | 18 ++-- CHANGELOG.rst | 3 +- README.rst | 2 +- meson.build | 86 +++++++++---------- meson_options.txt | 2 +- pyproject.toml | 12 ++- scripts/bump_libbson.py | 28 ++---- scripts/fetch_mongo_c_driver.py | 2 +- 13 files changed, 119 insertions(+), 104 deletions(-) diff --git a/.github/actions/setup-ccache/action.yml b/.github/actions/setup-ccache/action.yml index 5d43cd6..01ee12b 100644 --- a/.github/actions/setup-ccache/action.yml +++ b/.github/actions/setup-ccache/action.yml @@ -45,7 +45,7 @@ runs: fi - name: Restore/save ccache - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ${{ github.workspace }}/.ccache key: ${{ inputs.key }} diff --git a/.github/actions/setup-mongo-c-driver/action.yml b/.github/actions/setup-mongo-c-driver/action.yml index 0cfc411..7fc548e 100644 --- a/.github/actions/setup-mongo-c-driver/action.yml +++ b/.github/actions/setup-mongo-c-driver/action.yml @@ -17,11 +17,11 @@ runs: shell: bash run: | set -euo pipefail - VERSION=$(python -c "import re,pathlib; print(re.search(r'mongo-c-driver-([0-9.]+)', pathlib.Path('meson.build').read_text()).group(1))") + VERSION=$(python -c "import re,pathlib; print(re.search(r\"mcd_version\s*=\s*'([0-9.]+)'\", pathlib.Path('meson.build').read_text()).group(1))") echo "version=$VERSION" >> "$GITHUB_OUTPUT" - name: Restore/save mongo-c-driver tarball - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: ${{ github.workspace }}/.mongo-c-driver key: mcd-${{ steps.pinned.outputs.version }}-${{ runner.os }}-${{ github.sha }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 4cd0093..f5b8ab7 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -26,21 +26,19 @@ jobs: include: - language: c-cpp build-mode: manual - # Configure with Meson so libbson's config.h/version.h are generated. - # Then compile only our own bsonjs.c against that tree. CodeQL's - # tracer only extracts files the compiler is actually invoked on, so - # keeping libbson itself out of scope means never compiling it here. + # Configure with Meson so libbson's config.h/version.h are generated + # and the extension's compile flags are computed in one place, then + # compile bsonjs.c with the exact command recorded in + # compile_commands.json so the flags cannot drift from meson.build. + # CodeQL's tracer only extracts files the compiler is actually + # invoked on, so filtering to bsonjs.c keeps libbson out of scope. manual-build-command: | pip install "meson>=1.3" ninja - MCD=$(python3 scripts/fetch_mongo_c_driver.py --print-srcdir) + python3 scripts/fetch_mongo_c_driver.py meson setup /tmp/codeql-build - PYINC=$(python3 -c "import sysconfig; print(sysconfig.get_paths()['include'])") - BSONJS_VERSION=$(python3 -c "import re; print(re.search(r'(?m)^version\s*=\s*\"([^\"]+)\"', open('pyproject.toml').read()).group(1))") - gcc -c bsonjs/bsonjs.c -o /tmp/bsonjs.o \ - -Ibsonjs -I"$MCD/src/libbson/src" -I"$MCD/src/libbson/src/bson" \ - -I/tmp/codeql-build -I"$MCD/src/common/src" \ - -I"$PYINC" \ - -DBSON_STATIC -DPy_LIMITED_API=0x030b0000 "-DBSONJS_VERSION=\"$BSONJS_VERSION\"" + cmd=$(jq -r '.[] | select(.file | endswith("bsonjs.c")) | .command' /tmp/codeql-build/compile_commands.json) + o=$(printf '%s' "$cmd" | grep -o '\-o [^ ]*' | cut -d' ' -f2) + (cd /tmp/codeql-build && mkdir -p "$(dirname "$o")" && bash -c "$cmd") - language: python build-mode: none - language: actions diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 244b882..220d660 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -43,8 +43,38 @@ jobs: restore-keys: ccache-${{ runner.os }}- - name: Set up mongo-c-driver uses: ./.github/actions/setup-mongo-c-driver - - uses: pypa/cibuildwheel@4726cd35bb13f7bde50cf2761f2499ac7b3aa32c # v4.1.1 - - uses: actions/upload-artifact@v7 + - name: Build wheels (macOS/Linux) + if: runner.os != 'Windows' + uses: pypa/cibuildwheel@4726cd35bb13f7bde50cf2761f2499ac7b3aa32c # v4.1.1 + - name: Build wheels (Windows AMD64) + if: runner.os == 'Windows' + uses: pypa/cibuildwheel@4726cd35bb13f7bde50cf2761f2499ac7b3aa32c # v4.1.1 + env: + CIBW_ARCHS: AMD64 + # Meson's --vsenv activates only the x64 toolset, and its Python + # dependency check rejects a 32-bit Python against an x64 compiler + # ("Need python for x86_64, but found x86"). Exporting the x86 toolset + # environment here makes Meson skip its own activation (VSINSTALLDIR is + # set) and detect the x86 compiler instead, so the host machine reads + # as x86 and matches the 32-bit Python. + - name: Export the x86 MSVC environment + if: runner.os == 'Windows' + shell: powershell + run: | + $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" + $vcvarsall = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -find "VC\Auxiliary\Build\vcvarsall.bat" | Select-Object -First 1 + if (-not $vcvarsall) { throw "vcvarsall.bat not found" } + cmd /s /c "`"$vcvarsall`" x86 && set" | ForEach-Object { + if ($_ -match '^([^=]+)=(.*)$') { + Add-Content -Path $env:GITHUB_ENV -Value "$($Matches[1])=$($Matches[2])" + } + } + - name: Build wheels (Windows x86) + if: runner.os == 'Windows' + uses: pypa/cibuildwheel@4726cd35bb13f7bde50cf2761f2499ac7b3aa32c # v4.1.1 + env: + CIBW_ARCHS: x86 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 with: name: ${{ matrix.os }}-wheel path: ./wheelhouse/*.whl @@ -64,7 +94,7 @@ jobs: run: | python -m pip install build python -m build --sdist - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 with: name: "sdist" path: dist/*.tar.gz @@ -74,13 +104,13 @@ jobs: name: Download Wheels steps: - name: Download all workflow run artifacts - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0 - name: Flatten directory working-directory: . run: | find . -mindepth 2 -type f -exec mv {} . \; find . -type d -empty -delete - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 with: name: all-dist-${{ github.run_id }} path: "./*" \ No newline at end of file diff --git a/.github/workflows/release-python.yml b/.github/workflows/release-python.yml index 6c674f1..16ff066 100644 --- a/.github/workflows/release-python.yml +++ b/.github/workflows/release-python.yml @@ -73,7 +73,7 @@ jobs: id-token: write steps: - name: Download all the dists - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0 with: name: all-dist-${{ github.run_id }} path: dist/ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 92bd522..0f2a6ce 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,13 +38,13 @@ jobs: - name: Set up mongo-c-driver uses: ./.github/actions/setup-mongo-c-driver - name: Install build dependencies - run: python -m pip install "meson-python>=0.17" "meson>=1.2" ninja + run: python -m pip install "meson-python>=0.17" "meson>=1.3" ninja - name: Build wheel run: python -m pip wheel --no-build-isolation --no-deps -w dist . - name: Show ccache stats if: runner.os != 'Windows' run: ccache -s - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 with: name: wheel-macos path: ./dist/*.whl @@ -67,13 +67,13 @@ jobs: - name: Set up mongo-c-driver uses: ./.github/actions/setup-mongo-c-driver - name: Install build dependencies - run: python -m pip install "meson-python>=0.17" "meson>=1.2" ninja + run: python -m pip install "meson-python>=0.17" "meson>=1.3" ninja - name: Build wheel run: python -m pip wheel --no-build-isolation --no-deps -w dist . - name: Show ccache stats if: runner.os != 'Windows' run: ccache -s - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 with: name: wheel-ubuntu path: ./dist/*.whl @@ -97,13 +97,13 @@ jobs: - name: Set up mongo-c-driver uses: ./.github/actions/setup-mongo-c-driver - name: Install build dependencies - run: python -m pip install "meson-python>=0.17" "meson>=1.2" ninja + run: python -m pip install "meson-python>=0.17" "meson>=1.3" ninja - name: Build wheel run: python -m pip wheel --no-build-isolation -Csetup-args=--vsenv --no-deps -w dist . - name: Show ccache stats if: runner.os != 'Windows' run: ccache -s - - uses: actions/upload-artifact@v7 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 with: name: wheel-windows path: ./dist/*.whl @@ -122,7 +122,7 @@ jobs: with: persist-credentials: false - name: Download wheel - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0 with: name: wheel-macos path: dist @@ -148,7 +148,7 @@ jobs: with: persist-credentials: false - name: Download wheel - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0 with: name: wheel-ubuntu path: dist @@ -174,7 +174,7 @@ jobs: with: persist-credentials: false - name: Download wheel - uses: actions/download-artifact@v8 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0 with: name: wheel-windows path: dist diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d035ba4..bad264f 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,8 +7,7 @@ Version 0.8.0 updates the bundled libbson to 2.5.3 and compiles it from the pinned mongo-c-driver release instead of vendoring C sources. The build now uses Meson and requires Python 3.11+ (up from 3.9), so the extension is built against the CPython 3.11 Limited API. Building from source downloads -the mongo-c-driver release tarball on first build. Windows wheels are now -64-bit only. +the mongo-c-driver release tarball on first build. For a detailed breakdown of what changed in each version of libbson see its changelog: https://github.com/mongodb/mongo-c-driver/blob/2.5.3/NEWS http://mongoc.org/libbson/2.5.3/ diff --git a/README.rst b/README.rst index f1e7583..933488e 100644 --- a/README.rst +++ b/README.rst @@ -157,4 +157,4 @@ With no argument the script uses the latest mongo-c-driver release and exits without making changes when the pinned version is already current. Pass a version to target a specific release:: - $ bash scripts/bump-libbson.sh 2.5.0 + $ bash scripts/bump-libbson.sh 2.5.3 diff --git a/meson.build b/meson.build index 31c0a71..32246b3 100644 --- a/meson.build +++ b/meson.build @@ -15,16 +15,17 @@ threads = dependency('threads') # _DEFAULT_SOURCE, and error.c uses strerror_l only when _XOPEN_SOURCE >= 700. # glibc derives both from _GNU_SOURCE; musl derives neither, so define them # explicitly. Windows has its own code path (guarded on _WIN32), so leave it out -# entirely there. +# entirely there. The feature probes below compile with the same macros so they +# test the environment libbson is actually compiled in. if host_machine.system() == 'darwin' - add_project_arguments('-D_POSIX_C_SOURCE=200809L', language: 'c') + feature_args = ['-D_POSIX_C_SOURCE=200809L'] elif host_machine.system() != 'windows' - add_project_arguments( - '-D_GNU_SOURCE', - '-D_DEFAULT_SOURCE', - '-D_XOPEN_SOURCE=700', - language: 'c', - ) + feature_args = ['-D_GNU_SOURCE', '-D_DEFAULT_SOURCE', '-D_XOPEN_SOURCE=700'] +else + feature_args = [] +endif +if feature_args.length() > 0 + add_project_arguments(feature_args, language: 'c') endif # --- libbson from source ------------------------------------------------ @@ -33,14 +34,15 @@ endif # fetches it here when absent. We compile only libbson (static), never # libmongoc, by listing its sources explicitly. # -# SHA256 of that tarball, read by scripts/fetch_mongo_c_driver.py and updated -# by scripts/bump_libbson.py alongside the version. +# The pinned release, read by scripts/fetch_mongo_c_driver.py. The SHA256 of +# that tarball is updated by scripts/bump_libbson.py alongside the version. +mcd_version = '2.5.3' mcd_sha256 = '5eb6f2297f5fbfcf0bf79942623b84f883e9223f008f24497ae60937802a6315' mcd_src = get_option('mongo-c-driver-dir') if mcd_src == '' # Fallback: the CI action pre-fetches and extracts the release tarball here. - fallback = meson.project_source_root() / '.mongo-c-driver' / 'mongo-c-driver-2.5.3' + fallback = meson.project_source_root() / '.mongo-c-driver' / 'mongo-c-driver-@0@'.format(mcd_version) if not fs.is_dir(fallback) # Nothing pre-fetched, so fetch the pinned release now. This lets an # sdist install build itself without a separate setup step. @@ -58,51 +60,44 @@ if mcd_src == '' endif endif if mcd_src == '' or not fs.is_dir(mcd_src) - error('mongo-c-driver source dir not found. Set -Dmongo-c-driver-dir or extract the tarball to .mongo-c-driver/mongo-c-driver-2.5.3.') + error('mongo-c-driver source dir not found. Set -Dmongo-c-driver-dir or extract the tarball to .mongo-c-driver/mongo-c-driver-@0@.'.format(mcd_version)) endif bson_src_dir = mcd_src / 'src' / 'libbson' / 'src' common_src_dir = mcd_src / 'src' / 'common' / 'src' -# Generated headers (config.h, version.h) from the CMake templates. -libbson_major = 2 -libbson_minor = 5 -libbson_patch = 3 +# Generated headers (config.h, version.h) from the CMake templates. The +# feature probes mirror upstream's src/libbson/CMakeLists.txt, which runs +# CMake's check_include_file/check_symbol_exists/check_struct_has_member on +# every platform; cc.has_header()/cc.has_function()/cc.has_member() are the +# Meson equivalents. +mcd_ver_comps = mcd_version.split('.') +libbson_major = mcd_ver_comps[0].to_int() +libbson_minor = mcd_ver_comps[1].to_int() +libbson_patch = mcd_ver_comps[2].to_int() conf = configuration_data() if host_machine.endian() == 'little' conf.set('BSON_BYTE_ORDER', 1234) else conf.set('BSON_BYTE_ORDER', 4321) endif -if host_machine.system() == 'windows' - conf.set('BSON_OS', 2) - conf.set('BSON_HAVE_STRINGS_H', 0) - conf.set('BSON_HAVE_STRNLEN', 0) - conf.set('BSON_HAVE_CLOCK_GETTIME', 0) - conf.set('BSON_HAVE_GMTIME_R', 0) - conf.set('BSON_HAVE_RAND_R', 0) - conf.set('BSON_HAVE_TIMESPEC', 0) - conf.set('BSON_HAVE_ALIGNED_ALLOC', 0) - conf.set('BSON_HAVE_STRLCPY', 0) -else - conf.set('BSON_OS', 1) - conf.set('BSON_HAVE_STRINGS_H', 1) - conf.set('BSON_HAVE_STRNLEN', 1) - conf.set('BSON_HAVE_CLOCK_GETTIME', 1) - conf.set('BSON_HAVE_GMTIME_R', 1) - conf.set('BSON_HAVE_RAND_R', 1) - conf.set('BSON_HAVE_TIMESPEC', 1) - # aligned_alloc is C11 and is not declared under -std=c99. - conf.set('BSON_HAVE_ALIGNED_ALLOC', 0) - conf.set('BSON_HAVE_STRLCPY', host_machine.system() == 'darwin') -endif -conf.set('BSON_HAVE_STDBOOL_H', 1) -conf.set('BSON_HAVE_SNPRINTF', 1) +conf.set('BSON_OS', host_machine.system() == 'windows' ? 2 : 1) +conf.set('BSON_HAVE_STRINGS_H', cc.has_header('strings.h') ? 1 : 0) +conf.set('BSON_HAVE_STRNLEN', cc.has_function('strnlen', prefix: '#include ', args: feature_args) ? 1 : 0) +conf.set('BSON_HAVE_CLOCK_GETTIME', cc.has_function('clock_gettime', prefix: '#include ', args: feature_args) ? 1 : 0) +conf.set('BSON_HAVE_GMTIME_R', cc.has_function('gmtime_r', prefix: '#include ', args: feature_args) ? 1 : 0) +conf.set('BSON_HAVE_RAND_R', cc.has_function('rand_r', prefix: '#include ', args: feature_args) ? 1 : 0) +conf.set('BSON_HAVE_TIMESPEC', cc.has_member('struct timespec', 'tv_sec', prefix: '#include ', args: feature_args) ? 1 : 0) +conf.set('BSON_HAVE_STRLCPY', cc.has_function('strlcpy', prefix: '#include ', args: feature_args) ? 1 : 0) +conf.set('BSON_HAVE_STDBOOL_H', cc.has_header('stdbool.h') ? 1 : 0) +conf.set('BSON_HAVE_SNPRINTF', cc.has_function('snprintf', prefix: '#include ', args: feature_args) ? 1 : 0) +# aligned_alloc is C11 and is not declared under -std=c99. +conf.set('BSON_HAVE_ALIGNED_ALLOC', 0) version_conf = configuration_data() version_conf.set('libbson_VERSION_MAJOR', libbson_major) version_conf.set('libbson_VERSION_MINOR', libbson_minor) version_conf.set('libbson_VERSION_PATCH', libbson_patch) -version_conf.set('libbson_VERSION_FULL', '2.5.3') +version_conf.set('libbson_VERSION_FULL', mcd_version) version_conf.set('libbson_VERSION_PRERELEASE', '') subdir('bson') # common-config.h is included by the common/ sources; the build dir is on the @@ -166,11 +161,10 @@ libbson = static_library('bson', # Windows; find_library resolves it to the import library (a bare 'ws2_32' # link arg would be treated as an .obj and fail with LNK1181). ws2_32 = cc.find_library('ws2_32', required: false) -# bsonjs.__version__ comes from this macro; keep it in sync with the pyproject -# version (meson.project_version() does not see meson-python's override). -bsonjs_version = run_command(py, '-c', - 'import re,pathlib;print(re.search(r\'version\\s*=\\s*"([^"]+)"\',pathlib.Path("pyproject.toml").read_text()).group(1))', - check: true).stdout().strip() +# bsonjs.__version__ comes from this macro. The project version is also the +# wheel metadata version: pyproject.toml declares the version dynamically and +# meson-python feeds it from here, so meson.build is the single source. +bsonjs_version = meson.project_version() py.extension_module( 'bsonjs', diff --git a/meson_options.txt b/meson_options.txt index 141b5ec..79b6822 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,2 +1,2 @@ option('mongo-c-driver-dir', type: 'string', value: '', - description: 'Path to the extracted mongo-c-driver 2.5.3 source tree') + description: 'Path to the extracted mongo-c-driver source tree') diff --git a/pyproject.toml b/pyproject.toml index 52b4168..395f9d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,9 @@ build-backend = "mesonpy" [project] name = "python-bsonjs" -version = "0.8.0.dev0" +# The version lives in meson.build (project()); meson-python feeds it here so +# the wheel metadata and the bsonjs.__version__ macro cannot drift apart. +dynamic = ["version"] description = "A library for converting between BSON and JSON." readme = "README.rst" license = { file = "LICENSE" } @@ -67,9 +69,11 @@ repair-wheel-command = [ # Meson picks MinGW gcc by default on GitHub Actions Windows; --vsenv makes it # set up the Visual Studio (MSVC) environment instead. config-settings = { "setup-args" = "--vsenv" } -# Build 64-bit wheels only: meson-python cannot find the 32-bit Python headers -# in the 32-bit cibuildwheel environment. -archs = ["AMD64"] +# Build both 64-bit and 32-bit wheels. The 32-bit build must run with the x86 +# MSVC toolset in the environment: meson's --vsenv only ever activates the +# x64 toolset, and it rejects a 32-bit Python against an x64 compiler. The +# dist.yml workflow activates vcvarsall x86 for the x86 cibuildwheel run. +archs = ["AMD64", "x86"] repair-wheel-command = [ "copy {wheel} {dest_dir}", "pipx run abi3audit --strict --report {wheel}", diff --git a/scripts/bump_libbson.py b/scripts/bump_libbson.py index af45bc0..0d776ee 100644 --- a/scripts/bump_libbson.py +++ b/scripts/bump_libbson.py @@ -23,11 +23,11 @@ def package_version(): - """Return the package release version (X.Y.Z) from pyproject.toml.""" - text = (REPO_ROOT / "pyproject.toml").read_text() - m = re.search(r'version\s*=\s*"([^"]+)"', text) + """Return the package release version (X.Y.Z) from meson.build.""" + text = (REPO_ROOT / "meson.build").read_text() + m = re.search(r"(? Date: Tue, 15 Sep 2026 12:59:36 -0500 Subject: [PATCH 69/69] Fix zizmor findings in the workflows Name the exact release each action SHA resolves to (v7.0.1 and v8.0.1), and give dist.yml and release-python.yml minimal top-level permissions so jobs no longer inherit the default token scope. --- .github/workflows/dist.yml | 11 +++++++---- .github/workflows/release-python.yml | 5 ++++- .github/workflows/test.yml | 12 ++++++------ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 220d660..386fa84 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -23,6 +23,9 @@ defaults: run: shell: bash -eux {0} +permissions: + contents: read + jobs: build_wheels: runs-on: ${{ matrix.os }} @@ -74,7 +77,7 @@ jobs: uses: pypa/cibuildwheel@4726cd35bb13f7bde50cf2761f2499ac7b3aa32c # v4.1.1 env: CIBW_ARCHS: x86 - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ matrix.os }}-wheel path: ./wheelhouse/*.whl @@ -94,7 +97,7 @@ jobs: run: | python -m pip install build python -m build --sdist - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: "sdist" path: dist/*.tar.gz @@ -104,13 +107,13 @@ jobs: name: Download Wheels steps: - name: Download all workflow run artifacts - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - name: Flatten directory working-directory: . run: | find . -mindepth 2 -type f -exec mv {} . \; find . -type d -empty -delete - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: all-dist-${{ github.run_id }} path: "./*" \ No newline at end of file diff --git a/.github/workflows/release-python.yml b/.github/workflows/release-python.yml index 16ff066..59a0714 100644 --- a/.github/workflows/release-python.yml +++ b/.github/workflows/release-python.yml @@ -29,6 +29,9 @@ defaults: run: shell: bash -eux {0} +permissions: + contents: read + jobs: pre-publish: environment: release @@ -73,7 +76,7 @@ jobs: id-token: write steps: - name: Download all the dists - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: all-dist-${{ github.run_id }} path: dist/ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0f2a6ce..58f8640 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,7 +44,7 @@ jobs: - name: Show ccache stats if: runner.os != 'Windows' run: ccache -s - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheel-macos path: ./dist/*.whl @@ -73,7 +73,7 @@ jobs: - name: Show ccache stats if: runner.os != 'Windows' run: ccache -s - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheel-ubuntu path: ./dist/*.whl @@ -103,7 +103,7 @@ jobs: - name: Show ccache stats if: runner.os != 'Windows' run: ccache -s - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: wheel-windows path: ./dist/*.whl @@ -122,7 +122,7 @@ jobs: with: persist-credentials: false - name: Download wheel - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: wheel-macos path: dist @@ -148,7 +148,7 @@ jobs: with: persist-credentials: false - name: Download wheel - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: wheel-ubuntu path: dist @@ -174,7 +174,7 @@ jobs: with: persist-credentials: false - name: Download wheel - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: wheel-windows path: dist