Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve cbor_describe formatting and code quality #285

Merged
merged 4 commits into from
Jun 3, 2023
Merged
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -4,6 +4,9 @@ Template:
Next
---------------------
- [Updated documentation to refer to RFC 8949](https://github.com/PJK/libcbor/issues/269)
- Improvements to `cbor_describe`
- [Bytestring data will now be printed as well](https://github.com/PJK/libcbor/pull/281) by [akallabeth](https://github.com/akallabeth)
- [Formatting consistency and clarity improvements](https://github.com/PJK/libcbor/pull/285)

0.10.2 (2023-01-31)
---------------------
55 changes: 30 additions & 25 deletions src/cbor.c
Original file line number Diff line number Diff line change
@@ -306,7 +306,7 @@ static void _cbor_type_marquee(FILE *out, char *label, int indent) {
}

static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
setlocale(LC_ALL, "");
const int indent_offset = 4;
switch (cbor_typeof(item)) {
case CBOR_TYPE_UINT: {
_cbor_type_marquee(out, "CBOR_TYPE_UINT", indent);
@@ -323,15 +323,16 @@ static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
case CBOR_TYPE_BYTESTRING: {
_cbor_type_marquee(out, "CBOR_TYPE_BYTESTRING", indent);
if (cbor_bytestring_is_indefinite(item)) {
fprintf(out, "Indefinite, with %zu chunks:\n",
fprintf(out, "Indefinite, Chunks: %zu, Chunk data:\n",
cbor_bytestring_chunk_count(item));
for (size_t i = 0; i < cbor_bytestring_chunk_count(item); i++)
_cbor_nested_describe(cbor_bytestring_chunks_handle(item)[i], out,
indent + 4);
indent + indent_offset);
} else {
const unsigned char* data = cbor_bytestring_handle(item);
fprintf(out, "Definite, length %zuB\n", cbor_bytestring_length(item));
fprintf(out, "%*s", indent + 4, " ");
const unsigned char *data = cbor_bytestring_handle(item);
fprintf(out, "Definite, Length: %zuB, Data:\n",
cbor_bytestring_length(item));
fprintf(out, "%*s", indent + indent_offset, " ");
for (size_t i = 0; i < cbor_bytestring_length(item); i++)
fprintf(out, "%02x", (int)(data[i] & 0xff));
fprintf(out, "\n");
@@ -341,56 +342,60 @@ static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
case CBOR_TYPE_STRING: {
_cbor_type_marquee(out, "CBOR_TYPE_STRING", indent);
if (cbor_string_is_indefinite(item)) {
fprintf(out, "Indefinite, with %zu chunks:\n",
fprintf(out, "Indefinite, Chunks: %zu, Chunk data:\n",
cbor_string_chunk_count(item));
for (size_t i = 0; i < cbor_string_chunk_count(item); i++)
_cbor_nested_describe(cbor_string_chunks_handle(item)[i], out,
indent + 4);
indent + indent_offset);
} else {
fprintf(out, "Definite, length %zuB, %zu codepoints\n",
fprintf(out, "Definite, Length: %zuB, Codepoints: %zu, Data:\n",
cbor_string_length(item), cbor_string_codepoint_count(item));
/* Careful - this doesn't support multibyte characters! */
/* Printing those is out of the scope of this demo :) */
/* libICU is your friend */
// TODO: Handle escaping
fprintf(out, "%*s", indent + 4, " ");
/* XXX: no null at the end -> confused vprintf */
fwrite(cbor_string_handle(item), (int)cbor_string_length(item), 1, out);
fprintf(out, "%*s", indent + indent_offset, " ");
// Note: The string is not escaped, whitespace and control character
// will be printed in verbatim and take effect.
fwrite(cbor_string_handle(item), sizeof(unsigned char),
cbor_string_length(item), out);
fprintf(out, "\n");
}
break;
}
case CBOR_TYPE_ARRAY: {
_cbor_type_marquee(out, "CBOR_TYPE_ARRAY", indent);
if (cbor_array_is_definite(item)) {
fprintf(out, "Definite, size: %zu\n", cbor_array_size(item));
fprintf(out, "Definite, Size: %zu, Contents:\n", cbor_array_size(item));
} else {
fprintf(out, "Indefinite, size: %zu\n", cbor_array_size(item));
fprintf(out, "Indefinite, Size: %zu, Contents:\n",
cbor_array_size(item));
}

for (size_t i = 0; i < cbor_array_size(item); i++)
_cbor_nested_describe(cbor_array_handle(item)[i], out, indent + 4);
_cbor_nested_describe(cbor_array_handle(item)[i], out,
indent + indent_offset);
break;
}
case CBOR_TYPE_MAP: {
_cbor_type_marquee(out, "CBOR_TYPE_MAP", indent);
if (cbor_map_is_definite(item)) {
fprintf(out, "Definite, size: %zu\n", cbor_map_size(item));
fprintf(out, "Definite, Size: %zu, Contents:\n", cbor_map_size(item));
} else {
fprintf(out, "Indefinite, size: %zu\n", cbor_map_size(item));
fprintf(out, "Indefinite, Size: %zu, Contents:\n", cbor_map_size(item));
}

// TODO: Label and group keys and values
for (size_t i = 0; i < cbor_map_size(item); i++) {
_cbor_nested_describe(cbor_map_handle(item)[i].key, out, indent + 4);
_cbor_nested_describe(cbor_map_handle(item)[i].value, out, indent + 4);
fprintf(out, "%*sMap entry %zu\n", indent + indent_offset, " ", i);
_cbor_nested_describe(cbor_map_handle(item)[i].key, out,
indent + 2 * indent_offset);
_cbor_nested_describe(cbor_map_handle(item)[i].value, out,
indent + 2 * indent_offset);
}
break;
}
case CBOR_TYPE_TAG: {
_cbor_type_marquee(out, "CBOR_TYPE_TAG", indent);
fprintf(out, "Value: %" PRIu64 "\n", cbor_tag_value(item));
_cbor_nested_describe(cbor_move(cbor_tag_item(item)), out, indent + 4);
_cbor_nested_describe(cbor_move(cbor_tag_item(item)), out,
indent + indent_offset);
break;
}
case CBOR_TYPE_FLOAT_CTRL: {
@@ -406,7 +411,7 @@ static void _cbor_nested_describe(cbor_item_t *item, FILE *out, int indent) {
fprintf(out, "Simple value: %d\n", cbor_ctrl_value(item));
} else {
fprintf(out, "Width: %dB, ", _pow(2, cbor_float_get_width(item)));
fprintf(out, "value: %lf\n", cbor_float_get_float(item));
fprintf(out, "Value: %lf\n", cbor_float_get_float(item));
}
break;
}
162 changes: 80 additions & 82 deletions test/pretty_printer_test.c
Original file line number Diff line number Diff line change
@@ -11,52 +11,46 @@
#include "assertions.h"
#include "cbor.h"

void assert_describe_result(cbor_item_t *item, char *result[], size_t lines) {
void assert_describe_result(cbor_item_t *item, char *expected_result) {
#if CBOR_PRETTY_PRINTER
// We know the expected size based on `expected_result`, but read everything
// in order to get the full actual output in a useful error message.
const size_t buffer_size = 512;
FILE *outfile = tmpfile();
cbor_describe(item, outfile);
rewind(outfile);
for (size_t i = 0; i < lines; i++) {
// Expected line + linebreak + null character
size_t buffer_size = strlen(result[i]) + 2;
char *buffer = malloc(buffer_size);
char *result_with_newline = malloc(buffer_size);
assert_non_null(buffer);
assert_non_null(result_with_newline);
snprintf(result_with_newline, buffer_size, "%s\n", result[i]);
fgets(buffer, strlen(result[i]) + 2, outfile);
assert_int_equal(strlen(buffer), strlen(result_with_newline));
assert_string_equal(buffer, result_with_newline);
free(buffer);
free(result_with_newline);
}
fgetc(outfile);
// Treat string as null-terminated since cmocka doesn't have asserts
// for explicit length strings.
char *output = malloc(buffer_size);
assert_non_null(output);
size_t output_size = fread(output, sizeof(char), buffer_size, outfile);
output[output_size] = '\0';
assert_string_equal(output, expected_result);
assert_true(feof(outfile));
free(output);
fclose(outfile);
#endif
}

static void test_uint(void **_CBOR_UNUSED(_state)) {
cbor_item_t *item = cbor_build_uint8(42);
char *expected[] = {"[CBOR_TYPE_UINT] Width: 1B, Value: 42"};
assert_describe_result(item, expected, 1);
assert_describe_result(item, "[CBOR_TYPE_UINT] Width: 1B, Value: 42\n");
cbor_decref(&item);
}

static void test_negint(void **_CBOR_UNUSED(_state)) {
cbor_item_t *item = cbor_build_negint16(40);
char *expected[] = {"[CBOR_TYPE_NEGINT] Width: 2B, Value: -40 - 1"};
assert_describe_result(item, expected, 1);
assert_describe_result(item,
"[CBOR_TYPE_NEGINT] Width: 2B, Value: -40 - 1\n");
cbor_decref(&item);
}

static void test_definite_bytestring(void **_CBOR_UNUSED(_state)) {
unsigned char data[] = {0x01, 0x02, 0x03};
cbor_item_t *item = cbor_build_bytestring(data, 3);
char *expected[] = {
"[CBOR_TYPE_BYTESTRING] Definite, length 3B",
};
assert_describe_result(item, expected, 1);
assert_describe_result(item,
"[CBOR_TYPE_BYTESTRING] Definite, Length: 3B, Data:\n"
" 010203\n");
cbor_decref(&item);
}

@@ -67,24 +61,24 @@ static void test_indefinite_bytestring(void **_CBOR_UNUSED(_state)) {
item, cbor_move(cbor_build_bytestring(data, 3))));
assert_true(cbor_bytestring_add_chunk(
item, cbor_move(cbor_build_bytestring(data, 2))));
char *expected[] = {
"[CBOR_TYPE_BYTESTRING] Indefinite, with 2 chunks:",
" [CBOR_TYPE_BYTESTRING] Definite, length 3B",
" [CBOR_TYPE_BYTESTRING] Definite, length 2B",
};
assert_describe_result(item, expected, 3);
assert_describe_result(
item,
"[CBOR_TYPE_BYTESTRING] Indefinite, Chunks: 2, Chunk data:\n"
" [CBOR_TYPE_BYTESTRING] Definite, Length: 3B, Data:\n"
" 010203\n"
" [CBOR_TYPE_BYTESTRING] Definite, Length: 2B, Data:\n"
" 0102\n");
cbor_decref(&item);
}

static void test_definite_string(void **_CBOR_UNUSED(_state)) {
char *string = "Hello!";
cbor_item_t *item = cbor_build_string(string);
char *expected[] = {
// TODO: Codepoint metadata is not set by the builder, fix.
"[CBOR_TYPE_STRING] Definite, length 6B, 0 codepoints",
" Hello!",
};
assert_describe_result(item, expected, 2);
// TODO: Codepoint metadata is not set by the builder, fix.
assert_describe_result(
item,
"[CBOR_TYPE_STRING] Definite, Length: 6B, Codepoints: 0, Data:\n"
" Hello!\n");
cbor_decref(&item);
}

@@ -95,40 +89,47 @@ static void test_indefinite_string(void **_CBOR_UNUSED(_state)) {
cbor_string_add_chunk(item, cbor_move(cbor_build_string(string))));
assert_true(
cbor_string_add_chunk(item, cbor_move(cbor_build_string(string))));
char *expected[] = {
"[CBOR_TYPE_STRING] Indefinite, with 2 chunks:",
" [CBOR_TYPE_STRING] Definite, length 6B, 0 codepoints",
" Hello!",
" [CBOR_TYPE_STRING] Definite, length 6B, 0 codepoints",
" Hello!",
};
assert_describe_result(item, expected, 5);
assert_describe_result(
item,
"[CBOR_TYPE_STRING] Indefinite, Chunks: 2, Chunk data:\n"
" [CBOR_TYPE_STRING] Definite, Length: 6B, Codepoints: 0, Data:\n"
" Hello!\n"
" [CBOR_TYPE_STRING] Definite, Length: 6B, Codepoints: 0, Data:\n"
" Hello!\n");
cbor_decref(&item);
}

static void test_multibyte_string(void **_CBOR_UNUSED(_state)) {
// "Štěstíčko" in UTF-8
char *string = "\xc5\xa0t\xc4\x9bst\xc3\xad\xc4\x8dko";
cbor_item_t *item = cbor_build_string(string);
// TODO: Codepoint metadata is not set by the builder, fix.
assert_describe_result(
item,
"[CBOR_TYPE_STRING] Definite, Length: 13B, Codepoints: 0, Data:\n"
" \xc5\xa0t\xc4\x9bst\xc3\xad\xc4\x8dko\n");
cbor_decref(&item);
}

static void test_definite_array(void **_CBOR_UNUSED(_state)) {
cbor_item_t *item = cbor_new_definite_array(2);
assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(1))));
assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(2))));
char *expected[] = {
"[CBOR_TYPE_ARRAY] Definite, size: 2",
" [CBOR_TYPE_UINT] Width: 1B, Value: 1",
" [CBOR_TYPE_UINT] Width: 1B, Value: 2",
};
assert_describe_result(item, expected, 3);
assert_describe_result(item,
"[CBOR_TYPE_ARRAY] Definite, Size: 2, Contents:\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 1\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 2\n");
cbor_decref(&item);
}

static void test_indefinite_array(void **_CBOR_UNUSED(_state)) {
cbor_item_t *item = cbor_new_indefinite_array();
assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(1))));
assert_true(cbor_array_push(item, cbor_move(cbor_build_uint8(2))));
char *expected[] = {
"[CBOR_TYPE_ARRAY] Indefinite, size: 2",
" [CBOR_TYPE_UINT] Width: 1B, Value: 1",
" [CBOR_TYPE_UINT] Width: 1B, Value: 2",
};
assert_describe_result(item, expected, 3);
assert_describe_result(item,
"[CBOR_TYPE_ARRAY] Indefinite, Size: 2, Contents:\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 1\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 2\n");
cbor_decref(&item);
}

@@ -137,12 +138,12 @@ static void test_definite_map(void **_CBOR_UNUSED(_state)) {
assert_true(cbor_map_add(
item, (struct cbor_pair){.key = cbor_move(cbor_build_uint8(1)),
.value = cbor_move(cbor_build_uint8(2))}));
char *expected[] = {
"[CBOR_TYPE_MAP] Definite, size: 1",
" [CBOR_TYPE_UINT] Width: 1B, Value: 1",
" [CBOR_TYPE_UINT] Width: 1B, Value: 2",
};
assert_describe_result(item, expected, 3);
cbor_describe(item, stdout);
assert_describe_result(item,
"[CBOR_TYPE_MAP] Definite, Size: 1, Contents:\n"
" Map entry 0\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 1\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 2\n");
cbor_decref(&item);
}

@@ -151,22 +152,19 @@ static void test_indefinite_map(void **_CBOR_UNUSED(_state)) {
assert_true(cbor_map_add(
item, (struct cbor_pair){.key = cbor_move(cbor_build_uint8(1)),
.value = cbor_move(cbor_build_uint8(2))}));
char *expected[] = {
"[CBOR_TYPE_MAP] Indefinite, size: 1",
" [CBOR_TYPE_UINT] Width: 1B, Value: 1",
" [CBOR_TYPE_UINT] Width: 1B, Value: 2",
};
assert_describe_result(item, expected, 3);
assert_describe_result(item,
"[CBOR_TYPE_MAP] Indefinite, Size: 1, Contents:\n"
" Map entry 0\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 1\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 2\n");
cbor_decref(&item);
}

static void test_tag(void **_CBOR_UNUSED(_state)) {
cbor_item_t *item = cbor_build_tag(42, cbor_move(cbor_build_uint8(1)));
char *expected[] = {
"[CBOR_TYPE_TAG] Value: 42",
" [CBOR_TYPE_UINT] Width: 1B, Value: 1",
};
assert_describe_result(item, expected, 2);
assert_describe_result(item,
"[CBOR_TYPE_TAG] Value: 42\n"
" [CBOR_TYPE_UINT] Width: 1B, Value: 1\n");
cbor_decref(&item);
}

@@ -179,15 +177,14 @@ static void test_floats(void **_CBOR_UNUSED(_state)) {
cbor_array_push(item, cbor_move(cbor_build_ctrl(CBOR_CTRL_NULL))));
assert_true(cbor_array_push(item, cbor_move(cbor_build_ctrl(24))));
assert_true(cbor_array_push(item, cbor_move(cbor_build_float4(3.14f))));
char *expected[] = {
"[CBOR_TYPE_ARRAY] Indefinite, size: 5",
" [CBOR_TYPE_FLOAT_CTRL] Bool: true",
" [CBOR_TYPE_FLOAT_CTRL] Undefined",
" [CBOR_TYPE_FLOAT_CTRL] Null",
" [CBOR_TYPE_FLOAT_CTRL] Simple value: 24",
" [CBOR_TYPE_FLOAT_CTRL] Width: 4B, value: 3.140000",
};
assert_describe_result(item, expected, 6);
assert_describe_result(
item,
"[CBOR_TYPE_ARRAY] Indefinite, Size: 5, Contents:\n"
" [CBOR_TYPE_FLOAT_CTRL] Bool: true\n"
" [CBOR_TYPE_FLOAT_CTRL] Undefined\n"
" [CBOR_TYPE_FLOAT_CTRL] Null\n"
" [CBOR_TYPE_FLOAT_CTRL] Simple value: 24\n"
" [CBOR_TYPE_FLOAT_CTRL] Width: 4B, Value: 3.140000\n");
cbor_decref(&item);
}

@@ -199,6 +196,7 @@ int main(void) {
cmocka_unit_test(test_indefinite_bytestring),
cmocka_unit_test(test_definite_string),
cmocka_unit_test(test_indefinite_string),
cmocka_unit_test(test_multibyte_string),
cmocka_unit_test(test_definite_array),
cmocka_unit_test(test_indefinite_array),
cmocka_unit_test(test_definite_map),