Skip to content

Commit 57ba7c9

Browse files
cborpretty.c: Add an API to control display of encoding indicators
There are two flags: one to indicate whether maps, arrays and strings have indeterminate length, and one to indicate whether numbers and lengths have been encoded in longer form than necessary. Signed-off-by: Thiago Macieira <[email protected]>
1 parent 1410a50 commit 57ba7c9

File tree

5 files changed

+185
-64
lines changed

5 files changed

+185
-64
lines changed

src/cbor.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,10 +487,13 @@ enum CborPrettyFlags {
487487
CborPrettyNumericEncodingIndicators = 0x01,
488488
CborPrettyTextualEncodingIndicators = 0,
489489

490+
CborPrettyIndicateIndetermineLength = 0x02,
491+
CborPrettyIndicateOverlongNumbers = 0x04,
492+
490493
CborPrettyShowStringFragments = 0x100,
491494
CborPrettyMergeStringFragments = 0,
492495

493-
CborPrettyDefaultFlags = 0
496+
CborPrettyDefaultFlags = CborPrettyIndicateIndetermineLength
494497
};
495498

496499
CBOR_API CborError cbor_value_to_pretty_advance_flags(FILE *out, CborValue *value, int flags);

src/cborinternal_p.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,5 +80,6 @@ enum {
8080
};
8181

8282
CBOR_INTERNAL_API CBOR_INTERNAL_API_CC CborError _cbor_value_extract_number(const uint8_t **ptr, const uint8_t *end, uint64_t *len);
83+
CBOR_INTERNAL_API CBOR_INTERNAL_API_CC CborError _cbor_value_prepare_string_iteration(CborValue *it);
8384

8485
#endif /* CBORINTERNAL_P_H */

src/cborparser.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,27 @@ CborError cbor_value_calculate_string_length(const CborValue *value, size_t *len
940940
return _cbor_value_copy_string(value, NULL, len, NULL);
941941
}
942942

943+
static inline void prepare_string_iteration(CborValue *it)
944+
{
945+
if (!cbor_value_is_length_known(it)) {
946+
/* chunked string: we're before the first chunk;
947+
* advance to the first chunk */
948+
++it->ptr;
949+
it->flags |= CborIteratorFlag_IteratingStringChunks;
950+
}
951+
}
952+
953+
CBOR_INTERNAL_API_CC CborError _cbor_value_prepare_string_iteration(CborValue *it)
954+
{
955+
cbor_assert((it->flags & CborIteratorFlag_IteratingStringChunks) == 0);
956+
prepare_string_iteration(it);
957+
958+
/* are we at the end? */
959+
if (it->ptr == it->parser->end)
960+
return CborErrorUnexpectedEOF;
961+
return CborNoError;
962+
}
963+
943964
static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t *len)
944965
{
945966
CborError err;
@@ -957,9 +978,8 @@ static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t
957978
/* if the length was known, it wasn't chunked, so finish iteration */
958979
goto last_chunk;
959980
}
960-
} else if (!cbor_value_is_length_known(it)) {
961-
/* chunked string, we're before the first chunk */
962-
++it->ptr;
981+
} else {
982+
prepare_string_iteration(it);
963983
}
964984

965985
/* are we at the end? */

src/cborpretty.c

Lines changed: 103 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,32 @@
105105
* A dot is always present.
106106
* \par Arrays:
107107
* Comma-separated list of elements, enclosed in square brackets ("[" and "]").
108-
* If the array length is indeterminate, an underscore ("_") appears immediately after the opening bracket.
109108
* \par Maps:
110109
* Comma-separated list of key-value pairs, with the key and value separated
111110
* by a colon (":"), enclosed in curly braces ("{" and "}").
112-
* If the map length is indeterminate, an underscore ("_") appears immediately after the opening brace.
111+
*
112+
* The CborPrettyFlags enumerator contains flags to control some aspects of the
113+
* encoding:
114+
* \par String fragmentation
115+
* When the CborPrettyShowStringFragments option is active, text and byte
116+
* strings that are transmitted in fragments are shown instead inside
117+
* parentheses ("(" and ")") with no preceding number and each fragment is
118+
* displayed individually. If a tag precedes the string, then the output
119+
* will contain a double set of parentheses. If the option is not active,
120+
* the fragments are merged together and the display will not show any
121+
* difference from a string transmitted with determinate length.
122+
* \par Encoding indicators
123+
* Numbers and lengths in CBOR can be encoded in multiple representations.
124+
* If the CborPrettyIndicateOverlongNumbers option is active, numbers
125+
* and lengths that are transmitted in a longer encoding than necessary
126+
* will be indicated, by appending an underscore ("_") to either the
127+
* number or the opening bracket or brace, followed by a number
128+
* indicating the CBOR additional information: 0 for 1 byte, 1 for 2
129+
* bytes, 2 for 4 bytes and 3 for 8 bytes.
130+
* If the CborPrettyIndicateIndetermineLength option is active, maps,
131+
* arrays and strings encoded with indeterminate length will be marked by
132+
* an underscore after the opening bracket or brace or the string (if not
133+
* showing fragments), without a number after it.
113134
*/
114135

115136
/**
@@ -118,6 +139,8 @@
118139
*
119140
* \value CborPrettyNumericEncodingIndicators Use numeric encoding indicators instead of textual for float and half-float.
120141
* \value CborPrettyTextualEncodingIndicators Use textual encoding indicators for float ("f") and half-float ("f16").
142+
* \value CborPrettyIndicateIndetermineLength Indicate when a map or array has indeterminate length.
143+
* \value CborPrettyIndicateOverlongNumbers Indicate when a number or length was encoded with more bytes than needed.
121144
* \value CborPrettyShowStringFragments If the byte or text string is transmitted in chunks, show each individually.
122145
* \value CborPrettyMergeStringFragment Merge all chunked byte or text strings and display them in a single entry.
123146
* \value CborPrettyDefaultFlags Default conversion flags.
@@ -253,6 +276,56 @@ static CborError utf8EscapedDump(FILE *out, const void *ptr, size_t n)
253276
return CborNoError;
254277
}
255278

279+
static const char *resolve_indicator(const uint8_t *ptr, const uint8_t *end, int flags)
280+
{
281+
static const char indicators[8][3] = {
282+
"_0", "_1", "_2", "_3",
283+
"", "", "", /* these are not possible */
284+
"_"
285+
};
286+
const char *no_indicator = indicators[5]; /* empty string */
287+
uint8_t additional_information;
288+
uint8_t expected_information;
289+
uint64_t value;
290+
CborError err;
291+
292+
if (ptr == end)
293+
return NULL; /* CborErrorUnexpectedEOF */
294+
295+
additional_information = (*ptr & SmallValueMask);
296+
if (additional_information < Value8Bit)
297+
return no_indicator;
298+
299+
/* determine whether to show anything */
300+
if ((flags & CborPrettyIndicateIndetermineLength) &&
301+
additional_information == IndefiniteLength)
302+
return indicators[IndefiniteLength - Value8Bit];
303+
if ((flags & CborPrettyIndicateOverlongNumbers) == 0)
304+
return no_indicator;
305+
306+
err = _cbor_value_extract_number(&ptr, end, &value);
307+
if (err)
308+
return NULL; /* CborErrorUnexpectedEOF */
309+
310+
expected_information = Value8Bit - 1;
311+
if (value >= Value8Bit)
312+
++expected_information;
313+
if (value > 0xffU)
314+
++expected_information;
315+
if (value > 0xffffU)
316+
++expected_information;
317+
if (value > 0xffffffffU)
318+
++expected_information;
319+
return expected_information == additional_information ?
320+
no_indicator :
321+
indicators[additional_information - Value8Bit];
322+
}
323+
324+
static const char *get_indicator(const CborValue *it, int flags)
325+
{
326+
return resolve_indicator(it->ptr, it->parser->end, flags);
327+
}
328+
256329
static CborError value_to_pretty(FILE *out, CborValue *it, int flags);
257330
static CborError container_to_pretty(FILE *out, CborValue *it, CborType containerType, int flags)
258331
{
@@ -288,13 +361,11 @@ static CborError value_to_pretty(FILE *out, CborValue *it, int flags)
288361
case CborMapType: {
289362
/* recursive type */
290363
CborValue recursed;
364+
const char *indicator = get_indicator(it, flags);
365+
const char *space = *indicator ? " " : indicator;
291366

292-
if (fprintf(out, type == CborArrayType ? "[" : "{") < 0)
367+
if (fprintf(out, "%c%s%s", type == CborArrayType ? '[' : '{', indicator, space) < 0)
293368
return CborErrorIO;
294-
if (!cbor_value_is_length_known(it)) {
295-
if (fprintf(out, "_ ") < 0)
296-
return CborErrorIO;
297-
}
298369

299370
err = cbor_value_enter_container(it, &recursed);
300371
if (err) {
@@ -336,6 +407,8 @@ static CborError value_to_pretty(FILE *out, CborValue *it, int flags)
336407
return CborErrorIO;
337408
}
338409
}
410+
if (fprintf(out, "%s", get_indicator(it, flags)) < 0)
411+
return CborErrorIO;
339412
break;
340413
}
341414

@@ -347,16 +420,30 @@ static CborError value_to_pretty(FILE *out, CborValue *it, int flags)
347420
const char *separator = "";
348421
char close = '\'';
349422
char open[3] = "h'";
423+
const char *indicator = NULL;
350424

351425
if (type == CborTextStringType) {
352426
close = open[0] = '"';
353427
open[1] = '\0';
354428
}
355429

356-
if (fputs(showingFragments ? "(_ " : open, out) < 0)
357-
return CborErrorIO;
430+
if (showingFragments) {
431+
if (fputs("(_ ", out) < 0)
432+
return CborErrorIO;
433+
err = _cbor_value_prepare_string_iteration(it);
434+
if (err)
435+
return err;
436+
} else {
437+
if (fputs(open, out) < 0)
438+
return CborErrorIO;
439+
}
358440

359441
while (1) {
442+
if (showingFragments || indicator == NULL) {
443+
/* any iteration, except the second for a non-chunked string */
444+
indicator = resolve_indicator(it->ptr, it->parser->end, flags);
445+
}
446+
360447
err = _cbor_value_get_string_chunk(it, &ptr, &n, it);
361448
if (err)
362449
return err;
@@ -369,21 +456,23 @@ static CborError value_to_pretty(FILE *out, CborValue *it, int flags)
369456
if (err)
370457
return err;
371458
if (showingFragments) {
372-
if (fputc(close, out) < 0)
459+
if (fprintf(out, "%c%s", close, indicator) < 0)
373460
return CborErrorIO;
374461
separator = ", ";
375462
}
376463
}
377464

378-
if (fputc(showingFragments ? ')' : close, out) < 0)
465+
if (showingFragments && fputc(')', out) < 0)
466+
return CborErrorIO;
467+
if (!showingFragments && fprintf(out, "%c%s", close, indicator) < 0)
379468
return CborErrorIO;
380469
return CborNoError;
381470
}
382471

383472
case CborTagType: {
384473
CborTag tag;
385474
cbor_value_get_tag(it, &tag); /* can't fail */
386-
if (fprintf(out, "%" PRIu64 "(", tag) < 0)
475+
if (fprintf(out, "%" PRIu64 "%s(", tag, get_indicator(it, flags)) < 0)
387476
return CborErrorIO;
388477
err = cbor_value_advance_fixed(it);
389478
if (err)
@@ -397,8 +486,9 @@ static CborError value_to_pretty(FILE *out, CborValue *it, int flags)
397486
}
398487

399488
case CborSimpleType: {
489+
/* simple types can't fail and can't have overlong encoding */
400490
uint8_t simple_type;
401-
cbor_value_get_simple_type(it, &simple_type); /* can't fail */
491+
cbor_value_get_simple_type(it, &simple_type);
402492
if (fprintf(out, "simple(%" PRIu8 ")", simple_type) < 0)
403493
return CborErrorIO;
404494
break;

0 commit comments

Comments
 (0)