1#ifndef SIMDJSON_DOM_FRACTURED_JSON_INL_H
2#define SIMDJSON_DOM_FRACTURED_JSON_INL_H
4#include "simdjson/dom/fractured_json.h"
5#include "simdjson/dom/serialization.h"
6#include "simdjson/dom/element-inl.h"
7#include "simdjson/dom/array-inl.h"
8#include "simdjson/dom/object-inl.h"
9#include "simdjson/dom/parser-inl.h"
10#include "simdjson/padded_string.h"
11#include "simdjson/internal/json_structure_analyzer.h"
12#include "simdjson/internal/fractured_formatter.h"
18#include <unordered_map>
27inline element_metrics structure_analyzer::analyze(
const dom::element& elem,
28 const fractured_json_options& opts) {
29 current_opts_ = &opts;
30 return analyze_element(elem, 0);
33inline void structure_analyzer::clear() {
34 current_opts_ =
nullptr;
37inline element_metrics structure_analyzer::analyze_array(
const dom::array& arr,
38 const fractured_json_options& opts) {
39 current_opts_ = &opts;
40 return analyze_array(arr, 0);
43inline element_metrics structure_analyzer::analyze_object(
const dom::object& obj,
44 const fractured_json_options& opts) {
45 current_opts_ = &opts;
46 return analyze_object(obj, 0);
49inline element_metrics structure_analyzer::analyze_element(
const dom::element& elem,
size_t depth)
const {
50 switch (elem.type()) {
53 if (elem.get_array().get(arr) ==
SUCCESS) {
54 return analyze_array(arr, depth);
60 if (elem.get_object().get(obj) ==
SUCCESS) {
61 return analyze_object(obj, depth);
67 return analyze_scalar(elem);
69 return element_metrics{};
72inline element_metrics structure_analyzer::analyze_scalar(
const dom::element& elem)
const {
73 element_metrics metrics;
74 metrics.complexity = 0;
75 metrics.child_count = 0;
76 metrics.can_inline =
true;
78 switch (elem.type()) {
81 if (elem.get_string().get(str) ==
SUCCESS) {
82 metrics.estimated_inline_len = estimate_string_length(str);
88 if (elem.get_int64().get(val) ==
SUCCESS) {
89 metrics.estimated_inline_len = estimate_number_length(val);
95 if (elem.get_uint64().get(val) ==
SUCCESS) {
96 metrics.estimated_inline_len = estimate_number_length(val);
102 if (elem.get_double().get(val) ==
SUCCESS) {
103 metrics.estimated_inline_len = estimate_number_length(val);
109 if (elem.get_bool().get(val) ==
SUCCESS) {
110 metrics.estimated_inline_len = val ? 4 : 5;
115 metrics.estimated_inline_len = 4;
124inline element_metrics structure_analyzer::analyze_array(
const dom::array& arr,
125 size_t depth)
const {
126 element_metrics metrics;
127 metrics.complexity = 1;
128 metrics.estimated_inline_len = 2;
129 metrics.child_count = 0;
131 size_t max_child_complexity = 0;
134 for (dom::element child : arr) {
136 metrics.estimated_inline_len += 2;
140 element_metrics child_metrics = analyze_element(child, depth + 1);
141 metrics.estimated_inline_len += child_metrics.estimated_inline_len;
142 max_child_complexity = (std::max)(max_child_complexity, child_metrics.complexity);
143 metrics.child_count++;
144 metrics.children.push_back(std::move(child_metrics));
148 metrics.complexity = 1 + max_child_complexity;
151 bool use_bracket_padding = (max_child_complexity >= 1)
152 ? current_opts_->nested_bracket_padding : current_opts_->simple_bracket_padding;
153 if (use_bracket_padding && metrics.child_count > 0) {
154 metrics.estimated_inline_len += 2;
158 metrics.can_inline = metrics.complexity <= current_opts_->max_inline_complexity;
161 bool wants_table_columns =
162 (current_opts_->enable_table_format &&
163 max_child_complexity <= current_opts_->max_table_row_complexity) ||
164 (current_opts_->enable_compact_multiline &&
165 max_child_complexity <= current_opts_->max_compact_array_complexity);
166 if (wants_table_columns) {
167 metrics.is_uniform_array = check_array_uniformity(arr, metrics, depth);
173inline element_metrics structure_analyzer::analyze_object(
const dom::object& obj,
174 size_t depth)
const {
175 element_metrics metrics;
176 metrics.complexity = 1;
177 metrics.estimated_inline_len = 2;
178 metrics.child_count = 0;
180 size_t max_child_complexity = 0;
183 for (dom::key_value_pair field : obj) {
185 metrics.estimated_inline_len += 2;
190 metrics.estimated_inline_len += estimate_string_length(field.key) + 2;
192 element_metrics child_metrics = analyze_element(field.value, depth + 1);
193 metrics.estimated_inline_len += child_metrics.estimated_inline_len;
194 max_child_complexity = (std::max)(max_child_complexity, child_metrics.complexity);
195 metrics.child_count++;
196 metrics.children.push_back(std::move(child_metrics));
199 metrics.complexity = 1 + max_child_complexity;
202 bool use_bracket_padding = (max_child_complexity >= 1)
203 ? current_opts_->nested_bracket_padding : current_opts_->simple_bracket_padding;
204 if (use_bracket_padding && metrics.child_count > 0) {
205 metrics.estimated_inline_len += 2;
208 metrics.can_inline = metrics.complexity <= current_opts_->max_inline_complexity;
213inline size_t structure_analyzer::estimate_string_length(std::string_view s)
const {
216 if (c ==
'"' || c ==
'\\' ||
static_cast<unsigned char>(c) < 32) {
225inline size_t structure_analyzer::estimate_number_length(
double d)
const {
226 if (!std::isfinite(d)) {
227#if SIMDJSON_ENABLE_NAN_INF
241 int len = snprintf(buf,
sizeof(buf),
"%.17g", d);
242 return len > 0 ?
static_cast<size_t>(len) : 20;
245inline size_t structure_analyzer::estimate_number_length(int64_t i)
const {
246 if (i == 0)
return 1;
248 if (i == INT64_MIN)
return 20;
249 size_t len = (i < 0) ? 1 : 0;
250 int64_t abs_val = (i < 0) ? -i : i;
251 while (abs_val > 0) {
258inline size_t structure_analyzer::estimate_number_length(uint64_t u)
const {
259 if (u == 0)
return 1;
268inline table_column_type structure_analyzer::classify_table_value(
dom::element_type type) {
276 default:
return table_column_type::simple;
280inline void structure_analyzer::classify_and_measure(
281 const std::vector<std::pair<dom::element, const element_metrics*>>& values,
282 table_column_type& common,
size_t& max_width) {
283 common = table_column_type::unknown;
285 for (
const auto& v : values) {
286 table_column_type t = classify_table_value(v.first.type());
287 if (t != table_column_type::unknown) {
288 if (common == table_column_type::unknown) common = t;
289 else if (t != common) common = table_column_type::mixed;
292 max_width = (std::max)(max_width, v.second->estimated_inline_len);
297inline void structure_analyzer::build_table_columns(
298 const std::vector<std::pair<dom::element, const element_metrics*>>& values,
299 std::vector<table_column>& out_columns)
const {
301 if (values.empty()) {
305 table_column_type common = table_column_type::unknown;
306 for (
const auto& v : values) {
307 table_column_type t = classify_table_value(v.first.type());
308 if (t == table_column_type::unknown)
continue;
309 if (common == table_column_type::unknown) common = t;
310 else if (t != common) { common = table_column_type::mixed;
break; }
312 if (common != table_column_type::object && common != table_column_type::array) {
316 std::vector<std::vector<std::pair<dom::element, const element_metrics*>>> per_column_values;
318 if (common == table_column_type::object) {
319 std::unordered_map<std::string_view, size_t> column_index;
320 for (
const auto& v : values) {
323 if (v.first.get_object().get(obj) !=
SUCCESS)
continue;
325 size_t field_idx = 0;
326 for (dom::key_value_pair field : obj) {
327 auto it = column_index.find(field.key);
329 if (it == column_index.end()) {
330 col_idx = out_columns.size();
331 column_index.emplace(field.key, col_idx);
332 out_columns.emplace_back();
333 out_columns.back().key.assign(field.key.data(), field.key.size());
334 out_columns.back().key_width = estimate_string_length(field.key);
335 per_column_values.emplace_back();
337 col_idx = it->second;
339 const element_metrics* field_metrics = (v.second && field_idx < v.second->children.size())
340 ? &v.second->children[field_idx] : nullptr;
341 per_column_values[col_idx].emplace_back(field.value, field_metrics);
346 for (
const auto& v : values) {
349 if (v.first.get_array().get(sub_arr) !=
SUCCESS)
continue;
352 for (dom::element item : sub_arr) {
353 if (out_columns.size() <= idx) {
354 out_columns.emplace_back();
355 per_column_values.emplace_back();
357 const element_metrics* item_metrics = (v.second && idx < v.second->children.size())
358 ? &v.second->children[idx] : nullptr;
359 per_column_values[idx].emplace_back(item, item_metrics);
365 for (
size_t i = 0; i < out_columns.size(); i++) {
366 table_column_type col_type;
368 classify_and_measure(per_column_values[i], col_type, max_width);
369 out_columns[i].type = col_type;
370 out_columns[i].plain_width = max_width;
372 if (col_type == table_column_type::object || col_type == table_column_type::array) {
373 build_table_columns(per_column_values[i], out_columns[i].children);
376 out_columns[i].width = out_columns[i].children.empty() ? max_width : compute_columns_width(out_columns[i].children);
380inline bool structure_analyzer::check_array_uniformity(
const dom::array& arr,
381 element_metrics& metrics,
382 size_t depth)
const {
383 std::vector<std::pair<dom::element, const element_metrics*>> values;
384 values.reserve(metrics.child_count);
387 for (dom::element elem : arr) {
388 const element_metrics* row_metrics = (row_idx < metrics.children.size()) ? &metrics.children[row_idx] : nullptr;
389 values.emplace_back(elem, row_metrics);
393 build_table_columns(values, metrics.table_columns);
394 if (metrics.table_columns.empty()) {
396 table_column_type common;
398 classify_and_measure(values, common, max_width);
399 if (common != table_column_type::number && common != table_column_type::simple) {
402 metrics.scalar_column_type = common;
403 metrics.table_row_width = max_width;
404 metrics.table_row_width_full = max_width;
408 metrics.table_row_width_full = compute_columns_width(metrics.table_columns);
410 size_t row_indent_width = (depth + 1) * current_opts_->indent_spaces;
411 size_t budget = (row_indent_width + 1 >= current_opts_->max_total_line_length)
412 ? 0 : current_opts_->max_total_line_length - row_indent_width - 1;
414 size_t width = metrics.table_row_width_full;
415 while (width > budget && flatten_deepest_columns(metrics.table_columns)) {
416 recompute_column_widths(metrics.table_columns);
417 width = compute_columns_width(metrics.table_columns);
420 metrics.table_row_width = width;
424inline size_t structure_analyzer::compute_columns_width(
const std::vector<table_column>& columns)
const {
426 if (table_row_is_nested(columns) ? current_opts_->nested_bracket_padding : current_opts_->simple_bracket_padding) {
430 for (
const table_column& col : columns) {
431 if (!col.key.empty()) {
432 width += col.key_width;
433 width += current_opts_->colon_padding ? 2 : 1;
437 if (columns.size() > 1) {
438 width += (columns.size() - 1) * (current_opts_->comma_padding ? 2 : 1);
443inline size_t structure_analyzer::column_height(
const table_column& column) {
445 for (
const table_column& child : column.children) {
446 height = (std::max)(height, column_height(child));
448 return column.children.empty() ? 0 : height + 1;
451inline bool structure_analyzer::flatten_deepest_columns(std::vector<table_column>& columns) {
452 size_t max_height = 0;
453 for (
const table_column& col : columns) {
454 max_height = (std::max)(max_height, column_height(col));
457 bool changed =
false;
458 for (table_column& col : columns) {
459 if (column_height(col) != max_height || max_height == 0)
continue;
460 if (max_height == 1) {
461 col.children.clear();
462 col.width = col.plain_width;
465 changed |= flatten_deepest_columns(col.children);
471inline void structure_analyzer::recompute_column_widths(std::vector<table_column>& columns)
const {
472 for (table_column& col : columns) {
473 if (!col.children.empty()) {
474 recompute_column_widths(col.children);
475 col.width = compute_columns_width(col.children);
480inline layout_mode structure_analyzer::decide_layout(
const element_metrics& metrics,
482 const fractured_json_options& opts,
483 bool has_trailing_comma) {
484 if (metrics.child_count == 0) {
485 return layout_mode::single_line;
488 long long signed_depth =
static_cast<long long>(depth);
489 bool depth_allows_inline_or_compact = signed_depth > opts.always_expand_depth;
490 bool depth_allows_table = signed_depth >= opts.always_expand_depth;
493 size_t reserved_width = depth * opts.indent_spaces + (has_trailing_comma ? 1 : 0);
494 if (depth_allows_inline_or_compact && metrics.can_inline &&
495 metrics.estimated_inline_len + reserved_width <= opts.max_total_line_length) {
496 return layout_mode::single_line;
501 size_t row_indent_width = (depth + 1) * opts.indent_spaces;
505 bool compact_multiline_enabled = opts.enable_compact_multiline &&
506 metrics.complexity <= opts.max_compact_array_complexity + 1 &&
507 metrics.child_count >= opts.min_compact_array_row_items;
508 if (depth_allows_inline_or_compact && compact_multiline_enabled) {
509 bool aligned = metrics.is_uniform_array;
510 size_t comma_width = opts.comma_padding ? 2 : 1;
511 size_t avg_item_width;
513 avg_item_width = metrics.table_row_width_full + comma_width;
516 for (
const element_metrics& child : metrics.children) {
517 sum += child.estimated_inline_len;
519 avg_item_width = comma_width + sum / metrics.child_count;
522 size_t row_pack_space = (row_indent_width >= opts.max_total_line_length)
523 ? 0 : opts.max_total_line_length - row_indent_width;
524 if (avg_item_width * opts.min_compact_array_row_items <= row_pack_space) {
525 return layout_mode::compact_multiline;
530 if (depth_allows_table && opts.enable_table_format &&
531 metrics.is_uniform_array &&
532 metrics.table_row_width + 1 + row_indent_width <= opts.max_total_line_length) {
533 return layout_mode::table;
536 return layout_mode::expanded;
543inline fractured_formatter::fractured_formatter(
const fractured_json_options& opts)
546simdjson_inline
void fractured_formatter::print_newline() {
547 if (current_layout_ == layout_mode::single_line) {
551 current_line_length_ = 0;
554simdjson_inline
void fractured_formatter::print_indents(
size_t depth) {
555 if (current_layout_ == layout_mode::single_line) {
558 for (
size_t i = 0; i < depth * options_.indent_spaces; i++) {
560 current_line_length_++;
564simdjson_inline
void fractured_formatter::print_space() {
566 current_line_length_++;
569inline void fractured_formatter::set_layout_mode(layout_mode mode) {
570 current_layout_ = mode;
573inline layout_mode fractured_formatter::get_layout_mode()
const {
574 return current_layout_;
577inline void fractured_formatter::track_line_length(
size_t chars) {
578 current_line_length_ += chars;
581inline bool fractured_formatter::should_break_line(
size_t upcoming_length)
const {
582 return (current_line_length_ + upcoming_length) > options_.max_total_line_length;
585inline const fractured_json_options& fractured_formatter::options()
const {
593inline fractured_string_builder::fractured_string_builder(
const fractured_json_options& opts)
594 : format_(opts), analyzer_{}, options_(opts) {}
596inline void fractured_string_builder::append(
const dom::element& value) {
598 element_metrics root_metrics = analyzer_.analyze(value, options_);
601 format_element(value, root_metrics, 0);
604inline void fractured_string_builder::append(
const dom::array& value) {
606 element_metrics metrics = analyzer_.analyze_array(value, options_);
607 format_array(value, metrics, 0);
610inline void fractured_string_builder::append(
const dom::object& value) {
612 element_metrics metrics = analyzer_.analyze_object(value, options_);
613 format_object(value, metrics, 0);
616simdjson_inline
void fractured_string_builder::clear() {
621simdjson_inline std::string_view fractured_string_builder::str()
const {
622 return format_.str();
625inline void fractured_string_builder::format_element(
const dom::element& elem,
626 const element_metrics& metrics,
628 bool has_trailing_comma) {
629 switch (elem.type()) {
630 case dom::element_type::ARRAY: {
632 if (elem.get_array().get(arr) == SUCCESS) {
633 format_array(arr, metrics, depth, has_trailing_comma);
637 case dom::element_type::OBJECT: {
639 if (elem.get_object().get(obj) == SUCCESS) {
640 format_object(obj, metrics, depth, has_trailing_comma);
650inline void fractured_string_builder::format_array(
const dom::array& arr,
651 const element_metrics& metrics,
653 bool has_trailing_comma) {
654 layout_mode layout = structure_analyzer::decide_layout(metrics, depth, options_, has_trailing_comma);
656 case layout_mode::single_line:
657 format_array_inline(arr, metrics);
659 case layout_mode::compact_multiline:
660 format_array_compact_multiline(arr, metrics, depth);
662 case layout_mode::table:
663 format_array_as_table(arr, metrics, depth);
665 case layout_mode::expanded:
667 format_array_expanded(arr, metrics, depth);
672inline void fractured_string_builder::format_array_inline(
const dom::array& arr,
673 const element_metrics& metrics) {
674 scoped_single_line_mode single_line(format_);
676 format_.start_array();
680 size_t child_idx = 0;
681 for (dom::element elem : arr) {
685 if (options_.comma_padding) {
686 format_.print_space();
688 }
else if (bracket_padding_for(metrics)) {
689 format_.print_space();
692 const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
693 format_element(elem, child_metrics, 0);
697 if (bracket_padding_for(metrics) && !empty) {
698 format_.print_space();
703inline void fractured_string_builder::format_array_compact_multiline(
const dom::array& arr,
704 const element_metrics& metrics,
706 if (metrics.is_uniform_array) {
707 format_array_compact_multiline_aligned(arr, metrics, depth);
711 format_.start_array();
712 format_.print_newline();
713 format_.print_indents(depth + 1);
716 bool prev_item_was_expanded =
false;
717 size_t child_idx = 0;
719 for (dom::element elem : arr) {
720 const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
724 format_.track_line_length(1);
727 if (prev_item_was_expanded ||
728 format_.should_break_line(child_metrics.estimated_inline_len)) {
729 format_.print_newline();
730 format_.print_indents(depth + 1);
731 }
else if (options_.comma_padding) {
732 format_.print_space();
737 bool is_last = (child_idx + 1 == metrics.child_count);
738 layout_mode item_layout = structure_analyzer::decide_layout(child_metrics, depth + 1, options_, !is_last);
739 bool item_fits = item_layout == layout_mode::single_line;
742 scoped_single_line_mode single_line(format_);
743 format_element(elem, child_metrics, depth + 1, !is_last);
745 format_.track_line_length(child_metrics.estimated_inline_len);
747 format_element(elem, child_metrics, depth + 1, !is_last);
749 prev_item_was_expanded = !item_fits;
754 format_.print_newline();
755 format_.print_indents(depth);
759inline void fractured_string_builder::format_array_compact_multiline_aligned(
760 const dom::array& arr,
const element_metrics& metrics,
size_t depth) {
761 const std::vector<table_column>& columns = metrics.table_columns;
763 format_.start_array();
764 format_.print_newline();
765 format_.print_indents(depth + 1);
767 size_t indent_width = (depth + 1) * options_.indent_spaces;
768 size_t available_line_space = (indent_width >= options_.max_total_line_length)
769 ? 0 : options_.max_total_line_length - indent_width;
770 size_t comma_width = options_.comma_padding ? 2 : 1;
771 size_t remaining_line_space = available_line_space;
774 size_t child_idx = 0;
776 for (dom::element elem : arr) {
777 bool needs_comma = (child_idx + 1 < metrics.child_count);
778 size_t space_needed = metrics.table_row_width_full + (needs_comma ? comma_width : 0);
781 if (remaining_line_space < space_needed) {
782 format_.print_newline();
783 format_.print_indents(depth + 1);
784 remaining_line_space = available_line_space;
785 }
else if (options_.comma_padding) {
786 format_.print_space();
791 const element_metrics& row_metrics = child_metrics_at(metrics.children, child_idx);
792 if (columns.empty()) {
793 format_table_scalar_row(elem, row_metrics, metrics.table_row_width_full, depth + 1,
794 metrics.scalar_column_type);
796 format_table_row(elem, row_metrics, columns, depth + 1);
801 remaining_line_space -= (std::min)(remaining_line_space, space_needed);
805 format_.print_newline();
806 format_.print_indents(depth);
810inline void fractured_string_builder::format_table_row_columns(
811 const std::vector<table_column>& columns,
812 const std::vector<bool>& found,
813 const std::vector<dom::element>& values,
814 const std::vector<const element_metrics*>& value_metrics,
816 const size_t num_columns = columns.size();
817 size_t last_present_idx = num_columns;
818 for (
size_t i = 0; i < num_columns; i++) {
819 if (found[i]) last_present_idx = i;
822 size_t comma_width = options_.comma_padding ? 2 : 1;
824 for (
size_t col_idx = 0; col_idx < num_columns; col_idx++) {
825 const table_column& column = columns[col_idx];
826 const bool is_last_col = (col_idx == num_columns - 1);
828 if (found[col_idx]) {
829 if (!column.key.empty()) {
830 format_.key(column.key);
831 if (options_.colon_padding) {
832 format_.print_space();
836 bool needs_comma = !is_last_col && (col_idx < last_present_idx);
838 if (!column.children.empty()) {
843 if (column.type == table_column_type::object) {
845 if (values[col_idx].get_object().get(sub_obj) ==
SUCCESS) {
846 const element_metrics& sub_metrics = child_metrics_at(value_metrics[col_idx]);
847 format_table_object_row(sub_obj, sub_metrics, column.children, depth);
851 if (values[col_idx].get_array().get(sub_arr) == SUCCESS) {
852 const element_metrics& sub_metrics = child_metrics_at(value_metrics[col_idx]);
853 format_table_array_row(sub_arr, sub_metrics, column.children, depth);
859 if (options_.comma_padding) {
860 format_.print_space();
864 const element_metrics& vm = child_metrics_at(value_metrics[col_idx]);
865 format_table_leaf_value(values[col_idx], vm, column.width, column.type, needs_comma,
869 if (!is_last_col && !needs_comma) {
871 for (
size_t i = 0; i < comma_width; i++) {
872 format_.one_char(
' ');
876 size_t slot_width = column.width;
877 if (!column.key.empty()) {
878 slot_width += column.key_width + (options_.colon_padding ? 2 : 1);
880 for (
size_t i = 0; i < slot_width; i++) {
881 format_.one_char(
' ');
885 for (
size_t i = 0; i < comma_width; i++) {
886 format_.one_char(
' ');
893inline void fractured_string_builder::format_table_object_row(
894 const dom::object& obj,
const element_metrics& row_metrics,
895 const std::vector<table_column>& columns,
size_t depth) {
896 const size_t num_columns = columns.size();
897 std::vector<bool> found(num_columns,
false);
898 std::vector<dom::element> values(num_columns);
899 std::vector<const element_metrics*> value_metrics(num_columns,
nullptr);
901 for (
size_t col_idx = 0; col_idx < num_columns; col_idx++) {
902 size_t field_idx = 0;
903 for (dom::key_value_pair field : obj) {
904 if (field.key == columns[col_idx].key) {
905 found[col_idx] =
true;
906 values[col_idx] = field.value;
907 value_metrics[col_idx] = (field_idx < row_metrics.children.size())
908 ? &row_metrics.children[field_idx] : nullptr;
915 bool nested = table_row_is_nested(columns);
916 format_.start_object();
917 if (nested ? options_.nested_bracket_padding : options_.simple_bracket_padding) {
918 format_.print_space();
920 format_table_row_columns(columns, found, values, value_metrics, depth);
921 if (nested ? options_.nested_bracket_padding : options_.simple_bracket_padding) {
922 format_.print_space();
924 format_.end_object();
927inline void fractured_string_builder::format_table_array_row(
928 const dom::array& arr,
const element_metrics& row_metrics,
929 const std::vector<table_column>& columns,
size_t depth) {
930 const size_t num_columns = columns.size();
931 std::vector<bool> found(num_columns,
false);
932 std::vector<dom::element> values(num_columns);
933 std::vector<const element_metrics*> value_metrics(num_columns,
nullptr);
936 for (dom::element item : arr) {
937 if (idx >= num_columns)
break;
940 value_metrics[idx] = (idx < row_metrics.children.size()) ? &row_metrics.children[idx] : nullptr;
944 bool nested = table_row_is_nested(columns);
945 format_.start_array();
946 if (nested ? options_.nested_bracket_padding : options_.simple_bracket_padding) {
947 format_.print_space();
949 format_table_row_columns(columns, found, values, value_metrics, depth);
950 if (nested ? options_.nested_bracket_padding : options_.simple_bracket_padding) {
951 format_.print_space();
956inline void fractured_string_builder::format_table_row(
957 const dom::element& elem,
const element_metrics& row_metrics,
958 const std::vector<table_column>& columns,
size_t depth) {
959 if (elem.type() == dom::element_type::ARRAY) {
961 if (elem.get_array().get(arr) == SUCCESS) {
962 format_table_array_row(arr, row_metrics, columns, depth);
966 if (elem.get_object().get(obj) == SUCCESS) {
967 format_table_object_row(obj, row_metrics, columns, depth);
972inline bool fractured_string_builder::comma_goes_before_padding(table_column_type column_type)
const {
973 switch (options_.comma_placement) {
974 case table_comma_placement::before_padding:
return true;
975 case table_comma_placement::after_padding:
return false;
976 case table_comma_placement::before_padding_except_numbers:
978 return column_type != table_column_type::number;
982inline void fractured_string_builder::format_table_leaf_value(
983 const dom::element& elem,
const element_metrics& vm,
size_t width,
984 table_column_type column_type,
bool needs_comma,
bool add_comma_space,
size_t depth) {
985 bool comma_before_pad = needs_comma && comma_goes_before_padding(column_type);
986 bool comma_after_pad = needs_comma && !comma_before_pad;
988 bool right_align = column_type == table_column_type::number &&
989 options_.number_alignment == number_list_alignment::right;
991 size_t value_len = vm.estimated_inline_len;
993 size_t right_pad = 0;
995 left_pad = (width > value_len) ? width - value_len : 0;
996 comma_before_pad = needs_comma;
997 comma_after_pad =
false;
999 right_pad = (width > value_len) ? width - value_len : 0;
1002 for (
size_t i = 0; i < left_pad; i++) {
1003 format_.one_char(
' ');
1007 scoped_single_line_mode single_line(format_);
1008 format_element(elem, vm, depth);
1011 if (comma_before_pad) {
1014 for (
size_t i = 0; i < right_pad; i++) {
1015 format_.one_char(
' ');
1017 if (comma_after_pad) {
1020 if (needs_comma && add_comma_space && options_.comma_padding) {
1021 format_.print_space();
1025inline void fractured_string_builder::format_table_scalar_row(
1026 const dom::element& elem,
const element_metrics& row_metrics,
size_t width,
size_t depth,
1027 table_column_type column_type) {
1028 format_table_leaf_value(elem, row_metrics, width, column_type,
1029 false,
false, depth);
1032inline void fractured_string_builder::format_array_as_table(
const dom::array& arr,
1033 const element_metrics& metrics,
1035 if (!metrics.is_uniform_array) {
1036 format_array_expanded(arr, metrics, depth);
1039 const std::vector<table_column>& columns = metrics.table_columns;
1041 format_.start_array();
1042 format_.print_newline();
1044 bool first_row =
true;
1045 size_t child_idx = 0;
1046 for (dom::element elem : arr) {
1049 format_.print_newline();
1053 format_.print_indents(depth + 1);
1055 const element_metrics& row_metrics = child_metrics_at(metrics.children, child_idx);
1056 if (columns.empty()) {
1057 format_table_scalar_row(elem, row_metrics, metrics.table_row_width, depth + 1,
1058 metrics.scalar_column_type);
1060 format_table_row(elem, row_metrics, columns, depth + 1);
1065 format_.print_newline();
1066 format_.print_indents(depth);
1067 format_.end_array();
1070inline void fractured_string_builder::format_array_expanded(
const dom::array& arr,
1071 const element_metrics& metrics,
1073 format_.start_array();
1077 size_t child_idx = 0;
1079 for (dom::element elem : arr) {
1086 format_.print_newline();
1087 format_.print_indents(depth + 1);
1088 const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
1089 bool is_last = (child_idx + 1 == metrics.child_count);
1090 format_element(elem, child_metrics, depth + 1, !is_last);
1095 format_.print_newline();
1096 format_.print_indents(depth);
1098 format_.end_array();
1101inline void fractured_string_builder::format_object(
const dom::object& obj,
1102 const element_metrics& metrics,
1104 bool has_trailing_comma) {
1105 layout_mode layout = structure_analyzer::decide_layout(metrics, depth, options_, has_trailing_comma);
1106 if (layout == layout_mode::single_line) {
1107 format_object_inline(obj, metrics);
1109 format_object_expanded(obj, metrics, depth);
1113inline void fractured_string_builder::format_object_inline(
const dom::object& obj,
1114 const element_metrics& metrics) {
1115 scoped_single_line_mode single_line(format_);
1117 format_.start_object();
1121 size_t child_idx = 0;
1123 for (dom::key_value_pair field : obj) {
1127 if (options_.comma_padding) {
1128 format_.print_space();
1130 }
else if (bracket_padding_for(metrics)) {
1131 format_.print_space();
1135 format_.key(field.key);
1136 if (options_.colon_padding) {
1137 format_.print_space();
1139 const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
1140 format_element(field.value, child_metrics, 0);
1144 if (bracket_padding_for(metrics) && !empty) {
1145 format_.print_space();
1147 format_.end_object();
1150inline void fractured_string_builder::format_object_expanded(
const dom::object& obj,
1151 const element_metrics& metrics,
1153 format_.start_object();
1157 size_t child_idx = 0;
1159 for (dom::key_value_pair field : obj) {
1166 format_.print_newline();
1167 format_.print_indents(depth + 1);
1168 format_.key(field.key);
1169 if (options_.colon_padding) {
1170 format_.print_space();
1172 const element_metrics& child_metrics = child_metrics_at(metrics.children, child_idx);
1173 bool is_last = (child_idx + 1 == metrics.child_count);
1174 format_element(field.value, child_metrics, depth + 1, !is_last);
1179 format_.print_newline();
1180 format_.print_indents(depth);
1182 format_.end_object();
1185inline void fractured_string_builder::format_scalar(
const dom::element& elem) {
1186 switch (elem.type()) {
1187 case dom::element_type::STRING: {
1188 std::string_view str;
1189 if (elem.get_string().get(str) == SUCCESS) {
1190 format_.string(str);
1194 case dom::element_type::INT64: {
1196 if (elem.get_int64().get(val) == SUCCESS) {
1197 format_.number(val);
1201 case dom::element_type::UINT64: {
1203 if (elem.get_uint64().get(val) == SUCCESS) {
1204 format_.number(val);
1208 case dom::element_type::DOUBLE: {
1210 if (elem.get_double().get(val) == SUCCESS) {
1211 format_.number(val);
1215 case dom::element_type::BOOL: {
1217 if (elem.get_bool().get(val) == SUCCESS) {
1218 val ? format_.true_atom() : format_.false_atom();
1222 case dom::element_type::NULL_VALUE:
1223 format_.null_atom();
1230inline bool fractured_string_builder::bracket_padding_for(
const element_metrics& metrics)
const {
1231 return metrics.complexity >= 2 ? options_.nested_bracket_padding : options_.simple_bracket_padding;
1247 internal::fractured_string_builder sb(options);
1249 std::string_view result = sb.str();
1250 return std::string(result.data(), result.size());
1253#if SIMDJSON_EXCEPTIONS
1255std::string fractured_json(simdjson_result<T> x) {
1257 throw simdjson_error(x.error());
1263std::string
fractured_json(simdjson_result<T> x,
const fractured_json_options& options) {
1265 throw simdjson_error(x.error());
1286 auto error = parser.
parse(padded).get(doc);
1289 return std::string(json_str);
A persistent document parser.
simdjson_result< element > parse(const uint8_t *buf, size_t len, bool realloc_if_needed=true) &noexcept
Parse a JSON document and return a temporary reference to it.
element_type
The actual concrete type of a JSON element This is the type it is most easily cast to with get<>.
@ UINT64
uint64_t: any integer that fits in uint64_t but not int64_t
@ DOUBLE
double: Any number with a "." or "e" that fits in double.
The top level simdjson namespace, containing everything the library provides.
std::string fractured_json_string(std::string_view json_str)
Format a JSON string using FracturedJson formatting.
std::string fractured_json(T x)
Format JSON using FracturedJson formatting with default options.
Configuration options for FracturedJson formatting.
String with extra allocation for ease of use with parser::parse()