From 4ba1318f9f5ae82bbe76bdc3915b3b40ab649d3d Mon Sep 17 00:00:00 2001 From: Igor Prilepov Date: Thu, 17 Sep 2026 10:40:27 -0700 Subject: [PATCH 1/2] Add streaming generic BRF printer --- include/quarry/runtime_c/generic_brf.h | 33 ++++ runtime_c/CMakeLists.txt | 2 +- runtime_c/README.md | 11 ++ runtime_c/generic_brf_print.c | 223 +++++++++++++++++++++++++ tests/runtime_c/generic_runtime_test.c | 28 ++++ 5 files changed, 296 insertions(+), 1 deletion(-) create mode 100644 runtime_c/generic_brf_print.c diff --git a/include/quarry/runtime_c/generic_brf.h b/include/quarry/runtime_c/generic_brf.h index f194d9e..2eebd3f 100644 --- a/include/quarry/runtime_c/generic_brf.h +++ b/include/quarry/runtime_c/generic_brf.h @@ -413,6 +413,39 @@ quarry_brf_traversal_result_t quarry_brf_traverse( const quarry_brf_record_view_t*, quarry_brf_traversal_callback_t, void*, quarry_brf_traversal_workspace_t*, const quarry_brf_traversal_limits_t*); +typedef enum { + QUARRY_BRF_PRINT_COMPLETED = 0, + QUARRY_BRF_PRINT_INVALID_ARGUMENT, + QUARRY_BRF_PRINT_OUTPUT_ERROR, + QUARRY_BRF_PRINT_WORK_LIMIT, + QUARRY_BRF_PRINT_DEPTH_LIMIT, + QUARRY_BRF_PRINT_WORKSPACE_EXHAUSTED +} quarry_brf_print_result_t; + +typedef int (*quarry_brf_print_write_callback_t)(const char*, size_t, void*); + +typedef struct { + size_t indent_width; /* Spaces per nesting level; zero is valid. */ + size_t max_output_bytes; /* SIZE_MAX is the default when options are omitted. */ +} quarry_brf_print_options_t; + +typedef struct { + uint8_t* frames; /* Caller-owned storage; one byte per active print frame. */ + size_t frame_capacity; + size_t frame_count; + size_t output_bytes; +} quarry_brf_print_workspace_t; + +/* + * Streams deterministic diagnostic output through quarry_brf_traverse(). The + * writer returns zero on success and nonzero on failure. The output contains + * no ownership-bearing views and is not a serialization format. + */ +quarry_brf_print_result_t quarry_brf_print( + const quarry_brf_record_view_t*, quarry_brf_print_write_callback_t, void*, + quarry_brf_print_workspace_t*, quarry_brf_traversal_workspace_t*, + const quarry_brf_traversal_limits_t*, const quarry_brf_print_options_t*); + #ifdef __cplusplus } #endif diff --git a/runtime_c/CMakeLists.txt b/runtime_c/CMakeLists.txt index 2ea645a..8a19f5a 100644 --- a/runtime_c/CMakeLists.txt +++ b/runtime_c/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(quarry_runtime_c STATIC generic_brf.c generic_brf_api.c generic_brf_structures.c generic_brf_encoding.c - generic_brf_traversal.c) + generic_brf_traversal.c generic_brf_print.c) add_library(Quarry::runtime_c ALIAS quarry_runtime_c) set_target_properties(quarry_runtime_c PROPERTIES EXPORT_NAME runtime_c) target_compile_features(quarry_runtime_c INTERFACE c_std_99) diff --git a/runtime_c/README.md b/runtime_c/README.md index b13a7ed..49d556f 100644 --- a/runtime_c/README.md +++ b/runtime_c/README.md @@ -39,6 +39,17 @@ QBS currently stores enum type names and numeric enum values, but not symbolic names for individual enum values. These APIs therefore do not provide enum value names; consumers should render enum values numerically. +## Streaming BRF inspection + +`quarry_brf_print()` formats a validated BRF record by consuming the existing +`quarry_brf_traverse()` event stream. It does not parse BRF bytes independently +and is intended for diagnostics, logging, debugging, and development tools; +its output is not a serialization format. Output is sent incrementally to a +caller-provided writer callback, with caller-owned printer and traversal +workspaces. Reflective QBS names are used when available; minimal QBS falls +back to labels such as `record[1]` and `field[0]`. Strings are escaped and +bytes are rendered as hexadecimal. Enum values are rendered numerically. + **Status: scalar, enum, bounded string, bounded bytes, bounded array (of scalar, enum, bounded string, bounded bytes, or record elements), nested record fields, and compiler-resolved cross-namespace enum/record fields and diff --git a/runtime_c/generic_brf_print.c b/runtime_c/generic_brf_print.c new file mode 100644 index 0000000..15ddfcb --- /dev/null +++ b/runtime_c/generic_brf_print.c @@ -0,0 +1,223 @@ +#include "quarry/runtime_c/generic_brf.h" + +#include +#include +#include +#include + +enum { PRINT_RECORD = 1U, PRINT_ARRAY = 2U, PRINT_FIRST = 4U }; + +typedef struct { + quarry_brf_print_write_callback_t write; + void* context; + quarry_brf_print_workspace_t* workspace; + quarry_brf_print_options_t options; + quarry_brf_print_result_t result; +} print_context_t; + +static int put(print_context_t* context, const char* data, size_t size) { + if (context->result != QUARRY_BRF_PRINT_COMPLETED) + return 0; + if (size > context->options.max_output_bytes - context->workspace->output_bytes) { + context->result = QUARRY_BRF_PRINT_OUTPUT_ERROR; + return 0; + } + if (size != 0U && context->write(data, size, context->context) != 0) { + context->result = QUARRY_BRF_PRINT_OUTPUT_ERROR; + return 0; + } + context->workspace->output_bytes += size; + return 1; +} + +static int text(print_context_t* context, const char* value) { + return put(context, value, strlen(value)); +} + +static int formatted(print_context_t* context, const char* format, ...) { + char buffer[128]; + va_list arguments; + int size; + va_start(arguments, format); + size = vsnprintf(buffer, sizeof(buffer), format, arguments); + va_end(arguments); + if (size < 0 || (size_t)size >= sizeof(buffer)) { + context->result = QUARRY_BRF_PRINT_OUTPUT_ERROR; + return 0; + } + return put(context, buffer, (size_t)size); +} + +static int indent(print_context_t* context, size_t depth) { + const size_t width = context->options.indent_width; + for (size_t i = 0U; i < depth; ++i) + for (size_t j = 0U; j < width; ++j) + if (!put(context, " ", 1U)) + return 0; + return 1; +} + +static int name_or_index(print_context_t* context, const quarry_brf_record_view_t* record, + uint16_t field_index) { + quarry_string_view_t name = {0}; + const quarry_generic_status_t status = + quarry_qbs_field_name(record->qbs, record->schema, field_index, &name); + if (status == QUARRY_GENERIC_OK) + return put(context, name.data, name.size); + if (status != QUARRY_GENERIC_FIELD_ABSENT && status != QUARRY_GENERIC_FIELD_NOT_FOUND) { + context->result = QUARRY_BRF_PRINT_OUTPUT_ERROR; + return 0; + } + return formatted(context, "field[%u]", (unsigned)field_index); +} + +static int record_label(print_context_t* context, const quarry_brf_record_view_t* record, + bool array_element) { + quarry_string_view_t name = {0}; + if (!array_element) { + const quarry_generic_status_t status = + quarry_qbs_record_name(record->qbs, record->schema, &name); + if (status == QUARRY_GENERIC_OK) + return put(context, name.data, name.size) && text(context, " {"); + if (status != QUARRY_GENERIC_FIELD_ABSENT && status != QUARRY_GENERIC_FIELD_NOT_FOUND) { + context->result = QUARRY_BRF_PRINT_OUTPUT_ERROR; + return 0; + } + return formatted(context, "record[%" PRIu32 "] {", record->schema->record_id); + } + return text(context, "{"); +} + +static int string_value(print_context_t* context, quarry_string_view_t value) { + if (!text(context, "\"")) + return 0; + for (size_t i = 0U; i < value.size; ++i) { + const unsigned char c = (unsigned char)value.data[i]; + if (c == '\\' || c == '"') { + if (!put(context, "\\", 1U) || !put(context, (const char*)&c, 1U)) + return 0; + } else if (c == '\n' || c == '\r' || c == '\t') { + const char escaped = c == '\n' ? 'n' : (c == '\r' ? 'r' : 't'); + if (!put(context, "\\", 1U) || !put(context, &escaped, 1U)) + return 0; + } else if (c < 0x20U) { + if (!formatted(context, "\\u%04x", (unsigned)c)) + return 0; + } else if (!put(context, (const char*)&c, 1U)) { + return 0; + } + } + return text(context, "\""); +} + +static int scalar_value(print_context_t* context, const quarry_brf_scalar_t* value) { + switch (value->kind) { + case QUARRY_BRF_SCALAR_UINT: return formatted(context, "%" PRIu64, value->uint_value); + case QUARRY_BRF_SCALAR_INT: return formatted(context, "%" PRId64, value->int_value); + case QUARRY_BRF_SCALAR_BOOL: return text(context, value->bool_value ? "true" : "false"); + case QUARRY_BRF_SCALAR_FLOAT: return formatted(context, "%.9g", (double)value->float_value); + case QUARRY_BRF_SCALAR_DOUBLE: return formatted(context, "%.17g", value->double_value); + case QUARRY_BRF_SCALAR_ENUM: return formatted(context, "enum(%" PRId64 ")", value->int_value); + case QUARRY_BRF_SCALAR_STRING: return string_value(context, value->string_value); + case QUARRY_BRF_SCALAR_BYTES: + if (!text(context, "0x")) return 0; + for (size_t i = 0U; i < value->bytes_value.size; ++i) + if (!formatted(context, "%02x", value->bytes_value.data[i])) return 0; + return 1; + } + context->result = QUARRY_BRF_PRINT_OUTPUT_ERROR; + return 0; +} + +static quarry_brf_traversal_control_t print_event(const quarry_brf_traversal_event_t* event, + void* opaque) { + print_context_t* context = (print_context_t*)opaque; + quarry_brf_print_workspace_t* workspace = context->workspace; + uint8_t* frame = NULL; + if (event->kind == QUARRY_BRF_EVENT_RECORD_BEGIN) { + const bool array_element = workspace->frame_count != 0U && + (workspace->frames[workspace->frame_count - 1U] & 3U) == PRINT_ARRAY; + if (!record_label(context, &event->record, array_element)) return QUARRY_BRF_TRAVERSAL_STOP; + if (workspace->frame_count >= workspace->frame_capacity) { + context->result = QUARRY_BRF_PRINT_WORKSPACE_EXHAUSTED; + return QUARRY_BRF_TRAVERSAL_STOP; + } + workspace->frames[workspace->frame_count++] = PRINT_RECORD | PRINT_FIRST; + return QUARRY_BRF_TRAVERSAL_CONTINUE; + } + if (event->kind == QUARRY_BRF_EVENT_RECORD_END) { + frame = &workspace->frames[workspace->frame_count - 1U]; + if ((*frame & PRINT_FIRST) != 0U) { + if (!text(context, "}")) return QUARRY_BRF_TRAVERSAL_STOP; + } else if (!text(context, "\n") || !indent(context, event->depth) || !text(context, "}")) { + return QUARRY_BRF_TRAVERSAL_STOP; + } + --workspace->frame_count; + return QUARRY_BRF_TRAVERSAL_CONTINUE; + } + if (event->kind == QUARRY_BRF_EVENT_FIELD) { + if (!event->present) return QUARRY_BRF_TRAVERSAL_CONTINUE; + frame = &workspace->frames[workspace->frame_count - 1U]; + if ((*frame & PRINT_FIRST) == 0U && !text(context, "\n")) return QUARRY_BRF_TRAVERSAL_STOP; + *frame &= (uint8_t)~PRINT_FIRST; + if (!indent(context, event->depth + 1U) || !name_or_index(context, &event->record, event->field_index) || + !text(context, ": ")) return QUARRY_BRF_TRAVERSAL_STOP; + return QUARRY_BRF_TRAVERSAL_CONTINUE; + } + if (event->kind == QUARRY_BRF_EVENT_ARRAY_BEGIN) { + if (!text(context, "[") || workspace->frame_count >= workspace->frame_capacity) { + context->result = workspace->frame_count >= workspace->frame_capacity + ? QUARRY_BRF_PRINT_WORKSPACE_EXHAUSTED : context->result; + return QUARRY_BRF_TRAVERSAL_STOP; + } + workspace->frames[workspace->frame_count++] = PRINT_ARRAY | PRINT_FIRST; + return QUARRY_BRF_TRAVERSAL_CONTINUE; + } + if (event->kind == QUARRY_BRF_EVENT_ARRAY_ELEMENT) { + frame = &workspace->frames[workspace->frame_count - 1U]; + if ((*frame & PRINT_FIRST) == 0U && !text(context, ",")) return QUARRY_BRF_TRAVERSAL_STOP; + *frame &= (uint8_t)~PRINT_FIRST; + if (!text(context, "\n") || !indent(context, event->depth + 2U)) + return QUARRY_BRF_TRAVERSAL_STOP; + return QUARRY_BRF_TRAVERSAL_CONTINUE; + } + if (event->kind == QUARRY_BRF_EVENT_ARRAY_END) { + frame = &workspace->frames[workspace->frame_count - 1U]; + if ((*frame & PRINT_FIRST) == 0U && (!text(context, "\n") || !indent(context, event->depth + 1U))) + return QUARRY_BRF_TRAVERSAL_STOP; + if (!text(context, "]")) return QUARRY_BRF_TRAVERSAL_STOP; + --workspace->frame_count; + return QUARRY_BRF_TRAVERSAL_CONTINUE; + } + if (event->kind == QUARRY_BRF_EVENT_SCALAR && !scalar_value(context, &event->scalar)) + return QUARRY_BRF_TRAVERSAL_STOP; + return QUARRY_BRF_TRAVERSAL_CONTINUE; +} + +quarry_brf_print_result_t quarry_brf_print( + const quarry_brf_record_view_t* record, quarry_brf_print_write_callback_t write, void* output, + quarry_brf_print_workspace_t* workspace, quarry_brf_traversal_workspace_t* traversal_workspace, + const quarry_brf_traversal_limits_t* traversal_limits, const quarry_brf_print_options_t* options) { + print_context_t context; + quarry_brf_traversal_result_t traversal_result; + if (record == NULL || write == NULL || workspace == NULL || workspace->frames == NULL || + workspace->frame_capacity == 0U || traversal_workspace == NULL) + return QUARRY_BRF_PRINT_INVALID_ARGUMENT; + workspace->frame_count = 0U; + workspace->output_bytes = 0U; + context = (print_context_t){write, output, workspace, + options == NULL ? (quarry_brf_print_options_t){2U, SIZE_MAX} + : *options, + QUARRY_BRF_PRINT_COMPLETED}; + traversal_result = quarry_brf_traverse(record, print_event, &context, traversal_workspace, + traversal_limits); + if (context.result != QUARRY_BRF_PRINT_COMPLETED) + return context.result; + switch (traversal_result) { + case QUARRY_BRF_TRAVERSAL_COMPLETED: return QUARRY_BRF_PRINT_COMPLETED; + case QUARRY_BRF_TRAVERSAL_WORK_LIMIT: return QUARRY_BRF_PRINT_WORK_LIMIT; + case QUARRY_BRF_TRAVERSAL_DEPTH_LIMIT: return QUARRY_BRF_PRINT_DEPTH_LIMIT; + case QUARRY_BRF_TRAVERSAL_WORKSPACE_EXHAUSTED: return QUARRY_BRF_PRINT_WORKSPACE_EXHAUSTED; + default: return QUARRY_BRF_PRINT_INVALID_ARGUMENT; + } +} diff --git a/tests/runtime_c/generic_runtime_test.c b/tests/runtime_c/generic_runtime_test.c index f36a90c..0e86559 100644 --- a/tests/runtime_c/generic_runtime_test.c +++ b/tests/runtime_c/generic_runtime_test.c @@ -40,6 +40,20 @@ typedef struct { size_t count; } trace_observer_t; +typedef struct { + char data[8192]; + size_t size; +} print_output_t; + +static int collect_print_output(const char* data, size_t size, void* context) { + print_output_t* output = (print_output_t*)context; + if (size > sizeof(output->data) - output->size) + return 1; + memcpy(output->data + output->size, data, size); + output->size += size; + return 0; +} + static quarry_generic_status_t encode_fixture_field(const quarry_brf_value_provider_t* provider, uint16_t index, quarry_brf_value_t* out) { (void)provider; @@ -614,6 +628,20 @@ int main(void) { observer.count == 0U || observer.first != QUARRY_BRF_EVENT_RECORD_BEGIN || observer.last != QUARRY_BRF_EVENT_RECORD_END) return 1; + { + uint8_t print_frames[64]; + quarry_brf_print_workspace_t print_workspace = {print_frames, sizeof(print_frames), 0U, 0U}; + print_output_t output = {0}; + if (quarry_brf_print(&structural_record, collect_print_output, &output, &print_workspace, + &traversal_workspace, NULL, NULL) != QUARRY_BRF_PRINT_COMPLETED || + output.size == 0U || output.size >= sizeof(output.data)) + return 1; + output.data[output.size] = '\0'; + if (strstr(output.data, "Parent {") == NULL || strstr(output.data, "sequence: 42") == NULL || + strstr(output.data, "enabled: true") == NULL || strstr(output.data, "samples: [") == NULL || + strstr(output.data, "child: Child {") == NULL || strstr(output.data, "items: [") == NULL) + return 1; + } const size_t traversal_count = observer.count; trace_observer_t trace = {0}; quarry_brf_traversal_workspace_reset(&traversal_workspace); From 5e51131167894711db0ac97fae4bb309c6264266 Mon Sep 17 00:00:00 2001 From: Igor Prilepov Date: Thu, 17 Sep 2026 11:42:57 -0700 Subject: [PATCH 2/2] Fix generic BRF printer formatting and limits --- include/quarry/runtime_c/generic_brf.h | 8 ++++- runtime_c/generic_brf_print.c | 42 ++++++++++++++-------- tests/runtime_c/generic_runtime_test.c | 49 ++++++++++++++++++++++++-- 3 files changed, 81 insertions(+), 18 deletions(-) diff --git a/include/quarry/runtime_c/generic_brf.h b/include/quarry/runtime_c/generic_brf.h index 2eebd3f..f58fa3e 100644 --- a/include/quarry/runtime_c/generic_brf.h +++ b/include/quarry/runtime_c/generic_brf.h @@ -430,7 +430,13 @@ typedef struct { } quarry_brf_print_options_t; typedef struct { - uint8_t* frames; /* Caller-owned storage; one byte per active print frame. */ + uint8_t kind; + uint8_t first; + size_t indent; +} quarry_brf_print_frame_t; + +typedef struct { + quarry_brf_print_frame_t* frames; /* Caller-owned storage, one per active print frame. */ size_t frame_capacity; size_t frame_count; size_t output_bytes; diff --git a/runtime_c/generic_brf_print.c b/runtime_c/generic_brf_print.c index 15ddfcb..474514d 100644 --- a/runtime_c/generic_brf_print.c +++ b/runtime_c/generic_brf_print.c @@ -5,7 +5,7 @@ #include #include -enum { PRINT_RECORD = 1U, PRINT_ARRAY = 2U, PRINT_FIRST = 4U }; +enum { PRINT_RECORD = 1U, PRINT_ARRAY = 2U }; typedef struct { quarry_brf_print_write_callback_t write; @@ -13,6 +13,7 @@ typedef struct { quarry_brf_print_workspace_t* workspace; quarry_brf_print_options_t options; quarry_brf_print_result_t result; + size_t pending_record_indent; } print_context_t; static int put(print_context_t* context, const char* data, size_t size) { @@ -133,23 +134,27 @@ static quarry_brf_traversal_control_t print_event(const quarry_brf_traversal_eve void* opaque) { print_context_t* context = (print_context_t*)opaque; quarry_brf_print_workspace_t* workspace = context->workspace; - uint8_t* frame = NULL; + quarry_brf_print_frame_t* frame = NULL; if (event->kind == QUARRY_BRF_EVENT_RECORD_BEGIN) { const bool array_element = workspace->frame_count != 0U && - (workspace->frames[workspace->frame_count - 1U] & 3U) == PRINT_ARRAY; + workspace->frames[workspace->frame_count - 1U].kind == PRINT_ARRAY; if (!record_label(context, &event->record, array_element)) return QUARRY_BRF_TRAVERSAL_STOP; if (workspace->frame_count >= workspace->frame_capacity) { context->result = QUARRY_BRF_PRINT_WORKSPACE_EXHAUSTED; return QUARRY_BRF_TRAVERSAL_STOP; } - workspace->frames[workspace->frame_count++] = PRINT_RECORD | PRINT_FIRST; + const size_t record_indent = workspace->frame_count == 0U ? 0U : context->pending_record_indent; + workspace->frames[workspace->frame_count] = + (quarry_brf_print_frame_t){PRINT_RECORD, 1U, record_indent}; + ++workspace->frame_count; return QUARRY_BRF_TRAVERSAL_CONTINUE; } if (event->kind == QUARRY_BRF_EVENT_RECORD_END) { frame = &workspace->frames[workspace->frame_count - 1U]; - if ((*frame & PRINT_FIRST) != 0U) { + if (frame->first != 0U) { if (!text(context, "}")) return QUARRY_BRF_TRAVERSAL_STOP; - } else if (!text(context, "\n") || !indent(context, event->depth) || !text(context, "}")) { + } else if (!text(context, "\n") || !indent(context, frame->indent) || + !text(context, "}")) { return QUARRY_BRF_TRAVERSAL_STOP; } --workspace->frame_count; @@ -158,10 +163,12 @@ static quarry_brf_traversal_control_t print_event(const quarry_brf_traversal_eve if (event->kind == QUARRY_BRF_EVENT_FIELD) { if (!event->present) return QUARRY_BRF_TRAVERSAL_CONTINUE; frame = &workspace->frames[workspace->frame_count - 1U]; - if ((*frame & PRINT_FIRST) == 0U && !text(context, "\n")) return QUARRY_BRF_TRAVERSAL_STOP; - *frame &= (uint8_t)~PRINT_FIRST; - if (!indent(context, event->depth + 1U) || !name_or_index(context, &event->record, event->field_index) || + if (!text(context, "\n")) return QUARRY_BRF_TRAVERSAL_STOP; + frame->first = 0U; + if (!indent(context, frame->indent + 1U) || + !name_or_index(context, &event->record, event->field_index) || !text(context, ": ")) return QUARRY_BRF_TRAVERSAL_STOP; + context->pending_record_indent = frame->indent + 1U; return QUARRY_BRF_TRAVERSAL_CONTINUE; } if (event->kind == QUARRY_BRF_EVENT_ARRAY_BEGIN) { @@ -170,20 +177,24 @@ static quarry_brf_traversal_control_t print_event(const quarry_brf_traversal_eve ? QUARRY_BRF_PRINT_WORKSPACE_EXHAUSTED : context->result; return QUARRY_BRF_TRAVERSAL_STOP; } - workspace->frames[workspace->frame_count++] = PRINT_ARRAY | PRINT_FIRST; + const size_t array_indent = workspace->frames[workspace->frame_count - 1U].indent + 1U; + workspace->frames[workspace->frame_count] = + (quarry_brf_print_frame_t){PRINT_ARRAY, 1U, array_indent}; + ++workspace->frame_count; return QUARRY_BRF_TRAVERSAL_CONTINUE; } if (event->kind == QUARRY_BRF_EVENT_ARRAY_ELEMENT) { frame = &workspace->frames[workspace->frame_count - 1U]; - if ((*frame & PRINT_FIRST) == 0U && !text(context, ",")) return QUARRY_BRF_TRAVERSAL_STOP; - *frame &= (uint8_t)~PRINT_FIRST; - if (!text(context, "\n") || !indent(context, event->depth + 2U)) + if (frame->first == 0U && !text(context, ",")) return QUARRY_BRF_TRAVERSAL_STOP; + frame->first = 0U; + if (!text(context, "\n") || !indent(context, frame->indent + 1U)) return QUARRY_BRF_TRAVERSAL_STOP; + context->pending_record_indent = frame->indent + 1U; return QUARRY_BRF_TRAVERSAL_CONTINUE; } if (event->kind == QUARRY_BRF_EVENT_ARRAY_END) { frame = &workspace->frames[workspace->frame_count - 1U]; - if ((*frame & PRINT_FIRST) == 0U && (!text(context, "\n") || !indent(context, event->depth + 1U))) + if (frame->first == 0U && (!text(context, "\n") || !indent(context, frame->indent))) return QUARRY_BRF_TRAVERSAL_STOP; if (!text(context, "]")) return QUARRY_BRF_TRAVERSAL_STOP; --workspace->frame_count; @@ -208,7 +219,8 @@ quarry_brf_print_result_t quarry_brf_print( context = (print_context_t){write, output, workspace, options == NULL ? (quarry_brf_print_options_t){2U, SIZE_MAX} : *options, - QUARRY_BRF_PRINT_COMPLETED}; + QUARRY_BRF_PRINT_COMPLETED, + 0U}; traversal_result = quarry_brf_traverse(record, print_event, &context, traversal_workspace, traversal_limits); if (context.result != QUARRY_BRF_PRINT_COMPLETED) diff --git a/tests/runtime_c/generic_runtime_test.c b/tests/runtime_c/generic_runtime_test.c index 0e86559..df77434 100644 --- a/tests/runtime_c/generic_runtime_test.c +++ b/tests/runtime_c/generic_runtime_test.c @@ -45,6 +45,11 @@ typedef struct { size_t size; } print_output_t; +typedef struct { + size_t calls; + size_t fail_at; +} failing_print_writer_t; + static int collect_print_output(const char* data, size_t size, void* context) { print_output_t* output = (print_output_t*)context; if (size > sizeof(output->data) - output->size) @@ -54,6 +59,14 @@ static int collect_print_output(const char* data, size_t size, void* context) { return 0; } +static int fail_print_output(const char* data, size_t size, void* context) { + failing_print_writer_t* writer = (failing_print_writer_t*)context; + (void)data; + (void)size; + ++writer->calls; + return writer->calls == writer->fail_at ? 1 : 0; +} + static quarry_generic_status_t encode_fixture_field(const quarry_brf_value_provider_t* provider, uint16_t index, quarry_brf_value_t* out) { (void)provider; @@ -629,8 +642,8 @@ int main(void) { observer.last != QUARRY_BRF_EVENT_RECORD_END) return 1; { - uint8_t print_frames[64]; - quarry_brf_print_workspace_t print_workspace = {print_frames, sizeof(print_frames), 0U, 0U}; + quarry_brf_print_frame_t print_frames[64]; + quarry_brf_print_workspace_t print_workspace = {print_frames, 64U, 0U, 0U}; print_output_t output = {0}; if (quarry_brf_print(&structural_record, collect_print_output, &output, &print_workspace, &traversal_workspace, NULL, NULL) != QUARRY_BRF_PRINT_COMPLETED || @@ -641,6 +654,38 @@ int main(void) { strstr(output.data, "enabled: true") == NULL || strstr(output.data, "samples: [") == NULL || strstr(output.data, "child: Child {") == NULL || strstr(output.data, "items: [") == NULL) return 1; + { + print_output_t repeat = {0}; + quarry_brf_print_workspace_t repeat_workspace = {print_frames, 64U, 0U, 0U}; + if (quarry_brf_print(&structural_record, collect_print_output, &repeat, &repeat_workspace, + &traversal_workspace, NULL, NULL) != QUARRY_BRF_PRINT_COMPLETED || + repeat.size != output.size || memcmp(repeat.data, output.data, output.size) != 0) + return 1; + } + { + failing_print_writer_t failing = {0U, 2U}; + quarry_brf_print_workspace_t failing_workspace = {print_frames, 64U, 0U, 0U}; + if (quarry_brf_print(&structural_record, fail_print_output, &failing, &failing_workspace, + &traversal_workspace, NULL, NULL) != QUARRY_BRF_PRINT_OUTPUT_ERROR || + failing.calls != 2U) + return 1; + } + { + print_output_t exact = {0}; + quarry_brf_print_options_t exact_limit = {2U, output.size}; + quarry_brf_print_workspace_t limit_workspace = {print_frames, 64U, 0U, 0U}; + if (quarry_brf_print(&structural_record, collect_print_output, &exact, &limit_workspace, + &traversal_workspace, NULL, &exact_limit) != QUARRY_BRF_PRINT_COMPLETED || + exact.size != output.size || memcmp(exact.data, output.data, output.size) != 0) + return 1; + exact_limit.max_output_bytes = output.size - 1U; + limit_workspace = (quarry_brf_print_workspace_t){print_frames, 64U, 0U, 0U}; + print_output_t limited = {0}; + if (quarry_brf_print(&structural_record, collect_print_output, &limited, &limit_workspace, + &traversal_workspace, NULL, &exact_limit) != QUARRY_BRF_PRINT_OUTPUT_ERROR || + limited.size > exact_limit.max_output_bytes) + return 1; + } } const size_t traversal_count = observer.count; trace_observer_t trace = {0};