Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions include/quarry/runtime_c/generic_brf.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,23 @@ quarry_generic_status_t quarry_qbs_find_record_by_id(const quarry_qbs_view_t*, u
const quarry_qbs_record_view_t**);
quarry_generic_status_t quarry_qbs_find_record_by_name(const quarry_qbs_view_t*, const char*,
size_t, const quarry_qbs_record_view_t**);
/*
* Returns a non-owning view into the optional reflective QBS string section.
* The view remains valid while the input QBS byte buffer remains unchanged and
* alive. QUARRY_GENERIC_FIELD_ABSENT means that reflective strings are not
* available or that the descriptor has no name.
*/
quarry_generic_status_t quarry_qbs_get_string(const quarry_qbs_view_t*, uint16_t,
quarry_string_view_t*);
quarry_generic_status_t quarry_qbs_record_name(const quarry_qbs_view_t*,
const quarry_qbs_record_view_t*,
quarry_string_view_t*);
quarry_generic_status_t quarry_qbs_field_name(const quarry_qbs_view_t*,
const quarry_qbs_record_view_t*, uint16_t,
quarry_string_view_t*);
quarry_generic_status_t quarry_qbs_enum_name(const quarry_qbs_view_t*,
const quarry_qbs_enum_view_t*,
quarry_string_view_t*);
quarry_generic_status_t quarry_qbs_record_field(const quarry_qbs_view_t*,
const quarry_qbs_record_view_t*, uint16_t,
const quarry_qbs_field_view_t**);
Expand Down
21 changes: 21 additions & 0 deletions runtime_c/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,27 @@ sibling implementations: neither depends on the other, and each is
installed under its own canonical path (`include/quarry/runtime/` for C++,
`include/quarry/runtime_c/` for C -- see "CMake Package" below).

## Reflective QBS names

The generic QBS API exposes optional reflective names without exposing the
binary string-section representation. `quarry_qbs_get_string()` resolves a
string-table index, while `quarry_qbs_record_name()`,
`quarry_qbs_field_name()`, and `quarry_qbs_enum_name()` resolve descriptor
names through their existing `name_index` metadata. Each result is the public
`quarry_string_view_t` (`data` plus `size`), points into the caller-owned QBS
input buffer, is not NUL-terminated, and remains valid only while that buffer
is unchanged and alive.

Minimal QBS images do not contain reflective strings. Name lookup then returns
`QUARRY_GENERIC_FIELD_ABSENT`; the image remains valid and all structural
metadata remains usable. An invalid string index returns
`QUARRY_GENERIC_FIELD_NOT_FOUND`, while an invalid argument or malformed
string-table state returns the corresponding generic status.

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.

**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
Expand Down
73 changes: 73 additions & 0 deletions runtime_c/generic_brf.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,31 @@ static bool utf8(const uint8_t* p, size_t n) {
return true;
}

static quarry_generic_status_t string_section_entry(const quarry_qbs_view_t* q, uint16_t index,
size_t* data, size_t* length) {
if (q->bytes == NULL || q->strings_offset == 0U || q->strings_size < 8U ||
q->strings_offset > q->size || q->strings_size > q->size - q->strings_offset)
return QUARRY_GENERIC_MALFORMED_QBS;
const uint8_t* section = q->bytes + q->strings_offset;
const uint32_t count = u32(section);
if (count > UINT16_MAX)
return QUARRY_GENERIC_MALFORMED_QBS;
if (index >= count)
return QUARRY_GENERIC_FIELD_NOT_FOUND;
const size_t offsets = 4U + ((size_t)count + 1U) * 4U;
if (offsets > q->strings_size)
return QUARRY_GENERIC_MALFORMED_QBS;
const size_t payload = q->strings_size - offsets;
const uint32_t begin = u32(section + 4U + (size_t)index * 4U);
const uint32_t end = u32(section + 4U + ((size_t)index + 1U) * 4U);
if (u32(section + 4U) != 0U ||
u32(section + 4U + (size_t)count * 4U) != payload || begin > end || end > payload)
return QUARRY_GENERIC_MALFORMED_QBS;
*data = q->strings_offset + offsets + begin;
*length = (size_t)(end - begin);
return QUARRY_GENERIC_OK;
}

static quarry_generic_status_t section(const uint8_t* b, size_t n, uint16_t kind, size_t* at,
size_t* size) {
if (n < 40U || u16(b + 28U) > 64U)
Expand Down Expand Up @@ -226,6 +251,54 @@ quarry_generic_status_t quarry_qbs_find_record_by_name(const quarry_qbs_view_t*
}
return QUARRY_GENERIC_FIELD_NOT_FOUND;
}

quarry_generic_status_t quarry_qbs_get_string(const quarry_qbs_view_t* q, uint16_t index,
quarry_string_view_t* out) {
size_t data = 0U, length = 0U;
if (out != NULL)
*out = (quarry_string_view_t){NULL, 0U};
if (q == NULL || out == NULL)
return QUARRY_GENERIC_INVALID_ARGUMENT;
if (index == UINT16_MAX)
return QUARRY_GENERIC_FIELD_ABSENT;
if (q->strings_offset == 0U)
return QUARRY_GENERIC_FIELD_ABSENT;
const quarry_generic_status_t status = string_section_entry(q, index, &data, &length);
if (status != QUARRY_GENERIC_OK)
return status;
*out = (quarry_string_view_t){(const char*)q->bytes + data, length};
return QUARRY_GENERIC_OK;
}

quarry_generic_status_t quarry_qbs_record_name(const quarry_qbs_view_t* q,
const quarry_qbs_record_view_t* record,
quarry_string_view_t* out) {
if (q == NULL || record == NULL || out == NULL)
return QUARRY_GENERIC_INVALID_ARGUMENT;
return quarry_qbs_get_string(q, record->name_index, out);
}

quarry_generic_status_t quarry_qbs_field_name(const quarry_qbs_view_t* q,
const quarry_qbs_record_view_t* record, uint16_t index,
quarry_string_view_t* out) {
const quarry_qbs_field_view_t* field = NULL;
quarry_generic_status_t status;
if (q == NULL || record == NULL || out == NULL)
return QUARRY_GENERIC_INVALID_ARGUMENT;
status = quarry_qbs_record_field(q, record, index, &field);
if (status != QUARRY_GENERIC_OK)
return status;
return quarry_qbs_get_string(q, field->name_index, out);
}

quarry_generic_status_t quarry_qbs_enum_name(const quarry_qbs_view_t* q,
const quarry_qbs_enum_view_t* enumeration,
quarry_string_view_t* out) {
if (q == NULL || enumeration == NULL || out == NULL)
return QUARRY_GENERIC_INVALID_ARGUMENT;
return quarry_qbs_get_string(q, enumeration->name_index, out);
}

quarry_generic_status_t quarry_qbs_record_field(const quarry_qbs_view_t* q,
const quarry_qbs_record_view_t* r, uint16_t idx,
const quarry_qbs_field_view_t** out) {
Expand Down
27 changes: 27 additions & 0 deletions tests/runtime_c/generic_runtime_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,33 @@ int main(void) {
}
if (parent->field_count != 13U)
return 1;
quarry_string_view_t name;
if (quarry_qbs_record_name(&schema, parent, &name) != QUARRY_GENERIC_OK ||
name.size != 6U || memcmp(name.data, "Parent", name.size) != 0)
return 1;
if (quarry_qbs_field_name(&schema, parent, 0U, &name) != QUARRY_GENERIC_OK ||
name.size != 8U || memcmp(name.data, "sequence", name.size) != 0)
return 1;
if (schema.enum_count == 0U ||
quarry_qbs_enum_name(&schema, &schema.enums[0], &name) != QUARRY_GENERIC_OK ||
name.size != 5U || memcmp(name.data, "State", name.size) != 0)
return 1;
if (quarry_qbs_get_string(&schema, UINT16_MAX, &name) != QUARRY_GENERIC_FIELD_ABSENT ||
quarry_qbs_get_string(&schema, (uint16_t)schema.enum_count + 100U, &name) !=
QUARRY_GENERIC_FIELD_NOT_FOUND ||
quarry_qbs_get_string(NULL, 0U, &name) != QUARRY_GENERIC_INVALID_ARGUMENT ||
quarry_qbs_get_string(&schema, 0U, NULL) != QUARRY_GENERIC_INVALID_ARGUMENT ||
quarry_qbs_record_name(&schema, NULL, &name) != QUARRY_GENERIC_INVALID_ARGUMENT ||
quarry_qbs_field_name(&schema, parent, parent->field_count, &name) !=
QUARRY_GENERIC_FIELD_NOT_FOUND)
return 1;
quarry_qbs_view_t minimal_view = schema;
minimal_view.strings_offset = 0U;
minimal_view.strings_size = 0U;
if (quarry_qbs_get_string(&minimal_view, 0U, &name) != QUARRY_GENERIC_FIELD_ABSENT ||
quarry_qbs_record_name(&minimal_view, parent, &name) != QUARRY_GENERIC_FIELD_ABSENT ||
quarry_qbs_field_name(&minimal_view, parent, 0U, &name) != QUARRY_GENERIC_FIELD_ABSENT)
return 1;
quarry_brf_value_provider_t provider = {encode_fixture_field, NULL};
quarry_brf_encoder_field_t encoded_fields[13];
quarry_brf_encoder_workspace_t encoder_workspace =
Expand Down
Loading