From c2bede5845165205d7bfc6430ea30479d1eb4e99 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Thu, 5 May 2022 23:19:18 +0000 Subject: [PATCH 1/6] Support formatting BSON as pretty JSON (still private) --- src/libbson/src/bson/bson-json-private.h | 3 + src/libbson/src/bson/bson.c | 76 +++++++++++++++++----- src/libmongoc/tests/json-test-monitoring.c | 36 ++++++---- 3 files changed, 87 insertions(+), 28 deletions(-) diff --git a/src/libbson/src/bson/bson-json-private.h b/src/libbson/src/bson/bson-json-private.h index 9f51dcbd7e..003171b2a5 100644 --- a/src/libbson/src/bson/bson-json-private.h +++ b/src/libbson/src/bson/bson-json-private.h @@ -23,6 +23,9 @@ struct _bson_json_opts_t { bson_json_mode_t mode; int32_t max_len; + const char *initial_indent; + const char *subsequent_indent; + const char *level_indent; }; diff --git a/src/libbson/src/bson/bson.c b/src/libbson/src/bson/bson.c index 5af710e4dd..79a24af0a7 100644 --- a/src/libbson/src/bson/bson.c +++ b/src/libbson/src/bson/bson.c @@ -65,6 +65,7 @@ typedef struct { bson_json_mode_t mode; int32_t max_len; bool max_len_reached; + const bson_json_opts_t *opts; } bson_json_state_t; @@ -84,8 +85,7 @@ _bson_as_json_visit_document (const bson_iter_t *iter, static char * _bson_as_json_visit_all (const bson_t *bson, size_t *length, - bson_json_mode_t mode, - int32_t max_len); + const bson_json_opts_t *opts); /* * Globals. @@ -2510,6 +2510,24 @@ bson_equal (const bson_t *bson, const bson_t *other) } +static void +_bson_json_newline_indent (const bson_json_state_t *state) +{ + if (!state->opts->level_indent) { + return; + } + bson_string_append (state->str, "\n"); + if (state->opts->subsequent_indent) { + bson_string_append (state->str, state->opts->subsequent_indent); + } + if (state->opts->level_indent) { + for (int i = 0; i < state->depth + 1; ++i) { + bson_string_append (state->str, state->opts->level_indent); + } + } +} + + static bool _bson_as_json_visit_utf8 (const bson_iter_t *iter, const char *key, @@ -2909,6 +2927,8 @@ _bson_as_json_visit_before (const bson_iter_t *iter, bson_string_append (state->str, ", "); } + _bson_json_newline_indent (state); + if (state->keys) { escaped = bson_utf8_escape_for_json (key, -1); if (escaped) { @@ -2947,6 +2967,8 @@ _bson_as_json_visit_after (const bson_iter_t *iter, const char *key, void *data) return true; } + _bson_json_newline_indent (state); + return false; } @@ -3043,7 +3065,7 @@ _bson_as_json_visit_codewscope (const bson_iter_t *iter, max_scope_len = BSON_MAX (0, state->max_len - state->str->len); } - scope = _bson_as_json_visit_all (v_scope, NULL, state->mode, max_scope_len); + scope = _bson_as_json_visit_all (v_scope, NULL, state->opts); if (!scope) { return true; @@ -3090,10 +3112,16 @@ _bson_as_json_visit_document (const bson_iter_t *iter, return false; } + if (bson_empty (v_document)) { + bson_string_append (child_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.opts = state->opts; child_state.max_len = BSON_MAX_LEN_UNLIMITED; if (state->max_len != BSON_MAX_LEN_UNLIMITED) { child_state.max_len = BSON_MAX (0, state->max_len - state->str->len); @@ -3114,7 +3142,10 @@ _bson_as_json_visit_document (const bson_iter_t *iter, return !child_state.max_len_reached; } - bson_string_append (child_state.str, " }"); + child_state.depth -= 1; + _bson_json_newline_indent (&child_state); + bson_string_append (child_state.str, + state->opts->level_indent ? "}" : " }"); bson_string_append (state->str, child_state.str->str); bson_string_free (child_state.str, true); } @@ -3142,6 +3173,7 @@ _bson_as_json_visit_array (const bson_iter_t *iter, child_state.str = bson_string_new ("[ "); child_state.depth = state->depth + 1; child_state.mode = state->mode; + child_state.opts = state->opts; child_state.max_len = BSON_MAX_LEN_UNLIMITED; if (state->max_len != BSON_MAX_LEN_UNLIMITED) { child_state.max_len = BSON_MAX (0, state->max_len - state->str->len); @@ -3162,7 +3194,10 @@ _bson_as_json_visit_array (const bson_iter_t *iter, return !child_state.max_len_reached; } - bson_string_append (child_state.str, " ]"); + child_state.depth -= 1; + _bson_json_newline_indent (&child_state); + bson_string_append (child_state.str, + state->opts->level_indent ? "]" : " ]"); bson_string_append (state->str, child_state.str->str); bson_string_free (child_state.str, true); } @@ -3174,8 +3209,7 @@ _bson_as_json_visit_array (const bson_iter_t *iter, static char * _bson_as_json_visit_all (const bson_t *bson, size_t *length, - bson_json_mode_t mode, - int32_t max_len) + const bson_json_opts_t *opts) { bson_json_state_t state; bson_iter_t iter; @@ -3202,11 +3236,16 @@ _bson_as_json_visit_all (const bson_t *bson, state.count = 0; state.keys = true; - state.str = bson_string_new ("{ "); + if (state.opts->initial_indent) { + state.str = bson_string_new (state.opts->initial_indent); + bson_string_append_c (state.str, '{'); + } else { + state.str = bson_string_new ("{ "); + } state.depth = 0; state.err_offset = &err_offset; - state.mode = mode; - state.max_len = max_len; + state.opts = opts; + state.max_len = opts->max_len; state.max_len_reached = false; if ((bson_iter_visit_all (&iter, &bson_as_json_visitors, &state) || @@ -3222,11 +3261,16 @@ _bson_as_json_visit_all (const bson_t *bson, return NULL; } - /* Append closing space and } separately, in case we hit the max in between. */ + /* Append closing space and } separately, in case we hit the max in between. + */ remaining = state.max_len - state.str->len; - if (state.max_len == BSON_MAX_LEN_UNLIMITED || - remaining > 1) { - bson_string_append (state.str, " }"); + if (state.max_len == BSON_MAX_LEN_UNLIMITED || remaining > 1) { + if (state.opts->level_indent && state.opts->subsequent_indent) { + bson_string_append_printf ( + state.str, "\n%s}", state.opts->subsequent_indent); + } else { + bson_string_append (state.str, " }"); + } } else if (remaining == 1) { bson_string_append (state.str, " "); } @@ -3244,7 +3288,7 @@ bson_as_json_with_opts (const bson_t *bson, size_t *length, const bson_json_opts_t *opts) { - return _bson_as_json_visit_all (bson, length, opts->mode, opts->max_len); + return _bson_as_json_visit_all (bson, length, opts); } @@ -3286,7 +3330,7 @@ bson_array_as_json (const bson_t *bson, size_t *length) if (length) { *length = 0; - } + } if (bson_empty0 (bson)) { if (length) { diff --git a/src/libmongoc/tests/json-test-monitoring.c b/src/libmongoc/tests/json-test-monitoring.c index 1a22bcda0d..c7650acdfa 100644 --- a/src/libmongoc/tests/json-test-monitoring.c +++ b/src/libmongoc/tests/json-test-monitoring.c @@ -24,6 +24,7 @@ #include "mongoc/mongoc-topology-private.h" #include "mongoc/mongoc-util-private.h" #include "mongoc/mongoc-util-private.h" +#include #include "TestSuite.h" #include "test-conveniences.h" @@ -425,22 +426,33 @@ apm_match_visitor (match_ctx_t *ctx, return MATCH_ACTION_CONTINUE; } +static void +_print_bson_array_as_json (FILE *out, const bson_t *arr) +{ + bson_iter_t it; + for (bson_iter_init (&it, arr); bson_iter_next (&it);) { + bson_t elem; + bson_iter_bson (&it, &elem); + + bson_json_opts_t opts = {}; + opts.initial_indent = ""; + opts.level_indent = " "; + opts.subsequent_indent = " "; + opts.mode = BSON_JSON_MODE_CANONICAL; + opts.max_len = BSON_MAX_LEN_UNLIMITED; + char *str = bson_as_json_with_opts (&elem, NULL, &opts); + fprintf (out, " - %s\n", str); + bson_free (str); + } +} static void _apm_match_error_context (const bson_t *actual, const bson_t *expectations) { - char *actual_str; - char *expectations_str; - - actual_str = bson_as_canonical_extended_json (actual, NULL); - expectations_str = bson_as_canonical_extended_json (expectations, NULL); - fprintf (stderr, - "Error in APM matching\nFull list of captured events: %s\nFull " - "list of expectations: %s", - actual_str, - expectations_str); - bson_free (actual_str); - bson_free (expectations_str); + fprintf (stderr, "Error in APM matching.\nFull list of captured events:\n"); + _print_bson_array_as_json (stderr, actual); + fprintf (stderr, "Expected events:\n"); + _print_bson_array_as_json (stderr, expectations); } bool From 43dcf66bcd7f88663f55cf2023fa9de734ef62db Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 6 May 2022 00:10:05 +0000 Subject: [PATCH 2/6] Fix json tests --- src/libbson/src/bson/bson.c | 49 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/libbson/src/bson/bson.c b/src/libbson/src/bson/bson.c index 79a24af0a7..bdce3fa3b1 100644 --- a/src/libbson/src/bson/bson.c +++ b/src/libbson/src/bson/bson.c @@ -62,7 +62,6 @@ typedef struct { ssize_t *err_offset; uint32_t depth; bson_string_t *str; - bson_json_mode_t mode; int32_t max_len; bool max_len_reached; const bson_json_opts_t *opts; @@ -2560,7 +2559,7 @@ _bson_as_json_visit_int32 (const bson_iter_t *iter, { bson_json_state_t *state = data; - if (state->mode == BSON_JSON_MODE_CANONICAL) { + if (state->opts->mode == BSON_JSON_MODE_CANONICAL) { bson_string_append_printf ( state->str, "{ \"$numberInt\" : \"%" PRId32 "\" }", v_int32); } else { @@ -2579,7 +2578,7 @@ _bson_as_json_visit_int64 (const bson_iter_t *iter, { bson_json_state_t *state = data; - if (state->mode == BSON_JSON_MODE_CANONICAL) { + if (state->opts->mode == BSON_JSON_MODE_CANONICAL) { bson_string_append_printf ( state->str, "{ \"$numberLong\" : \"%" PRId64 "\" }", v_int64); } else { @@ -2622,8 +2621,8 @@ _bson_as_json_visit_double (const bson_iter_t *iter, /* 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 && + legacy = state->opts->mode == BSON_JSON_MODE_LEGACY || + (state->opts->mode == BSON_JSON_MODE_RELAXED && !(v_double != v_double || v_double * 0 != 0)); if (!legacy) { @@ -2715,8 +2714,8 @@ _bson_as_json_visit_binary (const bson_iter_t *iter, 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) { + if (state->opts->mode == BSON_JSON_MODE_CANONICAL || + state->opts->mode == BSON_JSON_MODE_RELAXED) { bson_string_append (state->str, "{ \"$binary\" : { \"base64\" : \""); bson_string_append (state->str, b64); bson_string_append (state->str, "\", \"subType\" : \""); @@ -2758,12 +2757,12 @@ _bson_as_json_visit_date_time (const bson_iter_t *iter, { bson_json_state_t *state = data; - if (state->mode == BSON_JSON_MODE_CANONICAL || - (state->mode == BSON_JSON_MODE_RELAXED && msec_since_epoch < 0)) { + if (state->opts->mode == BSON_JSON_MODE_CANONICAL || + (state->opts->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) { + } else if (state->opts->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, "\" }"); @@ -2792,8 +2791,8 @@ _bson_as_json_visit_regex (const bson_iter_t *iter, return true; } - if (state->mode == BSON_JSON_MODE_CANONICAL || - state->mode == BSON_JSON_MODE_RELAXED) { + if (state->opts->mode == BSON_JSON_MODE_CANONICAL || + state->opts->mode == BSON_JSON_MODE_RELAXED) { bson_string_append (state->str, "{ \"$regularExpression\" : { \"pattern\" : \""); bson_string_append (state->str, escaped); @@ -2850,8 +2849,8 @@ _bson_as_json_visit_dbpointer (const bson_iter_t *iter, return true; } - if (state->mode == BSON_JSON_MODE_CANONICAL || - state->mode == BSON_JSON_MODE_RELAXED) { + if (state->opts->mode == BSON_JSON_MODE_CANONICAL || + state->opts->mode == BSON_JSON_MODE_RELAXED) { bson_string_append (state->str, "{ \"$dbPointer\" : { \"$ref\" : \""); bson_string_append (state->str, escaped); bson_string_append (state->str, "\""); @@ -3019,8 +3018,8 @@ _bson_as_json_visit_symbol (const bson_iter_t *iter, return true; } - if (state->mode == BSON_JSON_MODE_CANONICAL || - state->mode == BSON_JSON_MODE_RELAXED) { + if (state->opts->mode == BSON_JSON_MODE_CANONICAL || + state->opts->mode == BSON_JSON_MODE_RELAXED) { bson_string_append (state->str, "{ \"$symbol\" : \""); bson_string_append (state->str, escaped); bson_string_append (state->str, "\" }"); @@ -3113,14 +3112,10 @@ _bson_as_json_visit_document (const bson_iter_t *iter, } if (bson_empty (v_document)) { - bson_string_append (child_state.str, "{ }"); - return false; - } - - if (bson_iter_init (&child, v_document)) { + child_state.str = bson_string_new ("{}"); + } else 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.opts = state->opts; child_state.max_len = BSON_MAX_LEN_UNLIMITED; if (state->max_len != BSON_MAX_LEN_UNLIMITED) { @@ -3146,6 +3141,8 @@ _bson_as_json_visit_document (const bson_iter_t *iter, _bson_json_newline_indent (&child_state); bson_string_append (child_state.str, state->opts->level_indent ? "}" : " }"); + } + if (child_state.str) { bson_string_append (state->str, child_state.str->str); bson_string_free (child_state.str, true); } @@ -3172,7 +3169,6 @@ _bson_as_json_visit_array (const bson_iter_t *iter, 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.opts = state->opts; child_state.max_len = BSON_MAX_LEN_UNLIMITED; if (state->max_len != BSON_MAX_LEN_UNLIMITED) { @@ -3236,6 +3232,7 @@ _bson_as_json_visit_all (const bson_t *bson, state.count = 0; state.keys = true; + state.opts = opts; if (state.opts->initial_indent) { state.str = bson_string_new (state.opts->initial_indent); bson_string_append_c (state.str, '{'); @@ -3244,7 +3241,6 @@ _bson_as_json_visit_all (const bson_t *bson, } state.depth = 0; state.err_offset = &err_offset; - state.opts = opts; state.max_len = opts->max_len; state.max_len_reached = false; @@ -3349,9 +3345,12 @@ bson_array_as_json (const bson_t *bson, size_t *length) state.str = bson_string_new ("[ "); state.depth = 0; state.err_offset = &err_offset; - state.mode = BSON_JSON_MODE_LEGACY; state.max_len = BSON_MAX_LEN_UNLIMITED; state.max_len_reached = false; + bson_json_opts_t opts = {0}; + opts.mode = BSON_JSON_MODE_LEGACY; + opts.max_len = BSON_MAX_LEN_UNLIMITED; + state.opts = &opts; if ((bson_iter_visit_all (&iter, &bson_as_json_visitors, &state) || err_offset != -1) && From 8f35ee38d6901ea74c97989ab9e62fc7f4c38bee Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 6 May 2022 00:23:23 +0000 Subject: [PATCH 3/6] zero-init bson_json_opts --- src/libbson/src/bson/bson-json.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libbson/src/bson/bson-json.c b/src/libbson/src/bson/bson-json.c index 474b3e9117..1ed284dfad 100644 --- a/src/libbson/src/bson/bson-json.c +++ b/src/libbson/src/bson/bson-json.c @@ -387,13 +387,12 @@ _noop (void) } - bson_json_opts_t * bson_json_opts_new (bson_json_mode_t mode, int32_t max_len) { bson_json_opts_t *opts; - opts = (bson_json_opts_t *) bson_malloc (sizeof *opts); + opts = (bson_json_opts_t *) bson_malloc0 (sizeof *opts); opts->mode = mode; opts->max_len = max_len; From acb440ec0ced18794a0e4a1db8d633a7bacb9eb5 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Fri, 6 May 2022 00:45:15 +0000 Subject: [PATCH 4/6] Less C99 --- src/libbson/src/bson/bson.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libbson/src/bson/bson.c b/src/libbson/src/bson/bson.c index bdce3fa3b1..eebcde987c 100644 --- a/src/libbson/src/bson/bson.c +++ b/src/libbson/src/bson/bson.c @@ -2520,7 +2520,8 @@ _bson_json_newline_indent (const bson_json_state_t *state) bson_string_append (state->str, state->opts->subsequent_indent); } if (state->opts->level_indent) { - for (int i = 0; i < state->depth + 1; ++i) { + int i = 0; + for (i = 0; i < state->depth + 1; ++i) { bson_string_append (state->str, state->opts->level_indent); } } From a94747fef20e83e1827d7295c869d843b5d5d2b2 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 18 May 2022 13:42:34 -0600 Subject: [PATCH 5/6] MIssing a zero --- src/libmongoc/tests/json-test-monitoring.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/tests/json-test-monitoring.c b/src/libmongoc/tests/json-test-monitoring.c index c7650acdfa..10f972fc1a 100644 --- a/src/libmongoc/tests/json-test-monitoring.c +++ b/src/libmongoc/tests/json-test-monitoring.c @@ -434,7 +434,7 @@ _print_bson_array_as_json (FILE *out, const bson_t *arr) bson_t elem; bson_iter_bson (&it, &elem); - bson_json_opts_t opts = {}; + bson_json_opts_t opts = {0}; opts.initial_indent = ""; opts.level_indent = " "; opts.subsequent_indent = " "; From ae25b439ab0d38faf41c1d855ac73dc7d4fe8fb8 Mon Sep 17 00:00:00 2001 From: vector-of-bool Date: Wed, 18 May 2022 13:46:07 -0600 Subject: [PATCH 6/6] Unused var --- src/libbson/src/bson/bson.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/libbson/src/bson/bson.c b/src/libbson/src/bson/bson.c index eebcde987c..ef9d180ea3 100644 --- a/src/libbson/src/bson/bson.c +++ b/src/libbson/src/bson/bson.c @@ -3047,7 +3047,6 @@ _bson_as_json_visit_codewscope (const bson_iter_t *iter, bson_json_state_t *state = data; char *code_escaped; char *scope; - int32_t max_scope_len = BSON_MAX_LEN_UNLIMITED; code_escaped = bson_utf8_escape_for_json (v_code, v_code_len); if (!code_escaped) { @@ -3060,11 +3059,6 @@ _bson_as_json_visit_codewscope (const bson_iter_t *iter, bson_free (code_escaped); - /* Encode scope with the same mode */ - if (state->max_len != BSON_MAX_LEN_UNLIMITED) { - max_scope_len = BSON_MAX (0, state->max_len - state->str->len); - } - scope = _bson_as_json_visit_all (v_scope, NULL, state->opts); if (!scope) {