Skip to content

Commit 34c8452

Browse files
WIP Initial API for delegated streaming in
Signed-off-by: Thiago Macieira <[email protected]>
1 parent 95129b8 commit 34c8452

File tree

7 files changed

+171
-22
lines changed

7 files changed

+171
-22
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ script:
7575
- make -s -f Makefile.configure configure | tee .config
7676
- make -k
7777
CFLAGS="$CFLAGS -march=native -g1 -Wall -Wextra -Werror"
78-
CPPFLAGS="-DNDEBUG"
78+
CPPFLAGS="-DNDEBUG -DCBOR_PARSER_READER_CONTROL=-1"
7979
lib/libtinycbor.a
8080
- size lib/libtinycbor.a | tee sizes
8181
- make -s clean

src/cbor.h

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ typedef enum CborError {
189189
CborErrorDataTooLarge = 1024,
190190
CborErrorNestingTooDeep,
191191
CborErrorUnsupportedType,
192+
CborErrorUnimplementedValidation,
192193

193194
/* errors in converting to JSON */
194195
CborErrorJsonObjectKeyIsAggregate = 1280,
@@ -267,6 +268,11 @@ CBOR_INLINE_API size_t cbor_encoder_get_extra_bytes_needed(const CborEncoder *en
267268

268269
/* Parser API */
269270

271+
enum CborParserGlobalFlags
272+
{
273+
CborParserFlag_ExternalSource = 0x01
274+
};
275+
270276
enum CborParserIteratorFlags
271277
{
272278
CborIteratorFlag_IntegerValueIs64Bit = 0x01,
@@ -278,17 +284,32 @@ enum CborParserIteratorFlags
278284
CborIteratorFlag_NextIsMapKey = 0x40
279285
};
280286

287+
struct CborValue;
288+
struct CborParserOperations
289+
{
290+
bool (*can_read_bytes)(void *token, size_t len);
291+
void *(*read_bytes)(void *token, void *dst, size_t offset, size_t len);
292+
void (*advance_bytes)(void *token, size_t len);
293+
CborError (*transfer_string)(void *token, const void **userptr, size_t offset, size_t len);
294+
};
295+
281296
struct CborParser
282297
{
283-
const uint8_t *end;
284-
uint32_t flags;
298+
union {
299+
const uint8_t *end;
300+
const struct CborParserOperations *ops;
301+
} source;
302+
enum CborParserGlobalFlags flags;
285303
};
286304
typedef struct CborParser CborParser;
287305

288306
struct CborValue
289307
{
290308
const CborParser *parser;
291-
const uint8_t *ptr;
309+
union {
310+
const uint8_t *ptr;
311+
void *token;
312+
} source;
292313
uint32_t remaining;
293314
uint16_t extra;
294315
uint8_t type;
@@ -298,13 +319,14 @@ typedef struct CborValue CborValue;
298319

299320
#ifndef CBOR_NO_PARSER_API
300321
CBOR_API CborError cbor_parser_init(const uint8_t *buffer, size_t size, uint32_t flags, CborParser *parser, CborValue *it);
322+
CBOR_API CborError cbor_parser_init_reader(const struct CborParserOperations *ops, CborParser *parser, CborValue *it, void *token);
301323

302324
CBOR_API CborError cbor_value_validate_basic(const CborValue *it);
303325

304326
CBOR_INLINE_API bool cbor_value_at_end(const CborValue *it)
305327
{ return it->remaining == 0; }
306328
CBOR_INLINE_API const uint8_t *cbor_value_get_next_byte(const CborValue *it)
307-
{ return it->ptr; }
329+
{ return it->source.ptr; }
308330
CBOR_API CborError cbor_value_advance_fixed(CborValue *it);
309331
CBOR_API CborError cbor_value_advance(CborValue *it);
310332
CBOR_INLINE_API bool cbor_value_is_container(const CborValue *it)

src/cborerrorstrings.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/****************************************************************************
22
**
3-
** Copyright (C) 2016 Intel Corporation
3+
** Copyright (C) 2021 Intel Corporation
44
**
55
** Permission is hereby granted, free of charge, to any person obtaining a copy
66
** of this software and associated documentation files (the "Software"), to deal
@@ -169,6 +169,9 @@ const char *cbor_error_string(CborError error)
169169
case CborErrorUnsupportedType:
170170
return _("unsupported type");
171171

172+
case CborErrorUnimplementedValidation:
173+
return _("validation not implemented for the current parser state");
174+
172175
case CborErrorJsonObjectKeyIsAggregate:
173176
return _("conversion to JSON failed: key in object is an array or map");
174177

src/cborinternal_p.h

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ static inline double decode_half(unsigned short half)
106106
# define CBOR_PARSER_MAX_RECURSIONS 1024
107107
#endif
108108

109+
#ifndef CBOR_PARSER_READER_CONTROL
110+
# define CBOR_PARSER_READER_CONTROL 0
111+
#endif
109112
/*
110113
* CBOR Major types
111114
* Encoded in the high 3 bits of the descriptor byte
@@ -158,25 +161,82 @@ CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_prepare_string_iter
158161

159162
static inline void copy_current_position(CborValue *dst, const CborValue *src)
160163
{
161-
dst->ptr = src->ptr;
164+
/* This "if" is here for pedantry only: the two branches should perform
165+
* the same memory operation. */
166+
if (src->parser->flags & CborParserFlag_ExternalSource)
167+
dst->source.token = src->source.token;
168+
else
169+
dst->source.ptr = src->source.ptr;
162170
}
163171

164172
static inline bool can_read_bytes(const CborValue *it, size_t n)
165173
{
174+
if (CBOR_PARSER_READER_CONTROL >= 0) {
175+
if (it->parser->flags & CborParserFlag_ExternalSource || CBOR_PARSER_READER_CONTROL != 0) {
176+
#ifdef CBOR_PARSER_CAN_READ_BYTES_FUNCTION
177+
return CBOR_PARSER_CAN_READ_BYTES_FUNCTION(it->source.token, n);
178+
#else
179+
return it->parser->source.ops->can_read_bytes(it->source.token, n);
180+
#endif
181+
}
182+
}
183+
166184
/* Convert the pointer subtraction to size_t since end >= ptr
167185
* (this prevents issues with (ptrdiff_t)n becoming negative).
168186
*/
169-
return (size_t)(it->parser->end - it->ptr) >= n;
187+
return (size_t)(it->parser->source.end - it->source.ptr) >= n;
170188
}
171189

172190
static inline void advance_bytes(CborValue *it, size_t n)
173191
{
174-
it->ptr += n;
192+
if (CBOR_PARSER_READER_CONTROL >= 0) {
193+
if (it->parser->flags & CborParserFlag_ExternalSource || CBOR_PARSER_READER_CONTROL != 0) {
194+
#ifdef CBOR_PARSER_ADVANCE_BYTES_FUNCTION
195+
CBOR_PARSER_ADVANCE_BYTES_FUNCTION(it->source.token, n);
196+
#else
197+
it->parser->source.ops->advance_bytes(it->source.token, n);
198+
#endif
199+
return;
200+
}
201+
}
202+
203+
it->source.ptr += n;
204+
}
205+
206+
static inline CborError transfer_string(CborValue *it, const void **ptr, size_t offset, size_t len)
207+
{
208+
if (CBOR_PARSER_READER_CONTROL >= 0) {
209+
if (it->parser->flags & CborParserFlag_ExternalSource || CBOR_PARSER_READER_CONTROL != 0) {
210+
#ifdef CBOR_PARSER_TRANSFER_STRING_FUNCTION
211+
return CBOR_PARSER_TRANSFER_STRING_FUNCTION(it->source.token, ptr, offset, len);
212+
#else
213+
return it->parser->source.ops->transfer_string(it->source.token, ptr, offset, len);
214+
#endif
215+
}
216+
}
217+
218+
it->source.ptr += offset;
219+
if (can_read_bytes(it, len)) {
220+
*CONST_CAST(const void **, ptr) = it->source.ptr;
221+
it->source.ptr += len;
222+
return CborNoError;
223+
}
224+
return CborErrorUnexpectedEOF;
175225
}
176226

177227
static inline void *read_bytes_unchecked(const CborValue *it, void *dst, size_t offset, size_t n)
178228
{
179-
return memcpy(dst, it->ptr + offset, n);
229+
if (CBOR_PARSER_READER_CONTROL >= 0) {
230+
if (it->parser->flags & CborParserFlag_ExternalSource || CBOR_PARSER_READER_CONTROL != 0) {
231+
#ifdef CBOR_PARSER_READ_BYTES_FUNCTION
232+
return CBOR_PARSER_READ_BYTES_FUNCTION(it->source.token, dst, offset, n);
233+
#else
234+
return it->parser->source.ops->read_bytes(it->source.token, dst, offset, n);
235+
#endif
236+
}
237+
}
238+
239+
return memcpy(dst, it->source.ptr + offset, n);
180240
}
181241

182242
#ifdef __GNUC__

src/cborparser.c

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -343,15 +343,26 @@ uint64_t _cbor_value_decode_int64_internal(const CborValue *value)
343343
CborError cbor_parser_init(const uint8_t *buffer, size_t size, uint32_t flags, CborParser *parser, CborValue *it)
344344
{
345345
memset(parser, 0, sizeof(*parser));
346-
parser->end = buffer + size;
347-
parser->flags = flags;
346+
parser->source.end = buffer + size;
347+
parser->flags = (enum CborParserGlobalFlags)flags;
348348
it->parser = parser;
349-
it->ptr = buffer;
349+
it->source.ptr = buffer;
350350
it->remaining = 1; /* there's one type altogether, usually an array or map */
351351
it->flags = 0;
352352
return preparse_value(it);
353353
}
354354

355+
CborError cbor_parser_init_reader(const struct CborParserOperations *ops, CborParser *parser, CborValue *it, void *token)
356+
{
357+
memset(parser, 0, sizeof(*parser));
358+
parser->source.ops = ops;
359+
parser->flags = CborParserFlag_ExternalSource;
360+
it->parser = parser;
361+
it->source.token = token;
362+
it->remaining = 1;
363+
return preparse_value(it);
364+
}
365+
355366
/**
356367
* \fn bool cbor_value_at_end(const CborValue *it)
357368
*
@@ -382,6 +393,11 @@ CborError cbor_parser_init(const uint8_t *buffer, size_t size, uint32_t flags, C
382393
* Note that the error recovery is not precise and the pointer may not indicate
383394
* the exact byte containing bad data.
384395
*
396+
* This function makes sense only when using a linear buffer (that is, when the
397+
* parser is initialize by cbor_parser_init()). If using an external source,
398+
* this function may return garbage; instead, consult the external source itself
399+
* to find out more details about the presence of more data.
400+
*
385401
* \sa cbor_value_at_end()
386402
*/
387403

@@ -1029,17 +1045,12 @@ static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t
10291045
++bytesNeeded;
10301046
}
10311047

1032-
*bufferptr = it->ptr + bytesNeeded;
10331048
if (*len != (size_t)*len)
10341049
return CborErrorDataTooLarge;
10351050

1036-
if (add_check_overflow(bytesNeeded, *len, &bytesNeeded))
1037-
return CborErrorUnexpectedEOF;
1038-
1039-
if (!can_read_bytes(it, bytesNeeded))
1040-
return CborErrorUnexpectedEOF;
1041-
1042-
advance_bytes(it, bytesNeeded);
1051+
CborError err = transfer_string(it, bufferptr, bytesNeeded, *len);
1052+
if (err)
1053+
return err;
10431054
} else {
10441055
return CborErrorIllegalType;
10451056
}

src/cborvalidation.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,8 @@ static CborError validate_container(CborValue *it, int containerType, uint32_t f
471471
continue;
472472

473473
if (flags & CborValidateMapIsSorted) {
474+
if (it->parser->flags & CborParserFlag_ExternalSource)
475+
return CborErrorUnimplementedValidation;
474476
if (previous) {
475477
size_t bytelen1 = (size_t)(previous_end - previous);
476478
size_t bytelen2 = (size_t)(cbor_value_get_next_byte(it) - current);

tests/parser/tst_parser.cpp

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/****************************************************************************
22
**
3-
** Copyright (C) 2019 Intel Corporation
3+
** Copyright (C) 2021 Intel Corporation
44
**
55
** Permission is hereby granted, free of charge, to any person obtaining a copy
66
** of this software and associated documentation files (the "Software"), to deal
@@ -85,6 +85,9 @@ private slots:
8585
void mapsAndArrays_data() { arrays_data(); }
8686
void mapsAndArrays();
8787

88+
void readerApi_data() { arrays_data(); }
89+
void readerApi();
90+
8891
// chunked string API
8992
void chunkedString_data();
9093
void chunkedString();
@@ -1038,6 +1041,54 @@ void tst_Parser::mapsAndArrays()
10381041
"{_ 1: [_ " + expected + "], \"Hello\": {_ " + expected + ": (_ )}}");
10391042
}
10401043

1044+
void tst_Parser::readerApi()
1045+
{
1046+
QFETCH(QByteArray, data);
1047+
QFETCH(QString, expected);
1048+
1049+
struct Input {
1050+
QByteArray data;
1051+
int consumed;
1052+
} input = { data, 0 };
1053+
1054+
CborParserOperations ops;
1055+
ops.can_read_bytes = [](void *token, size_t len) {
1056+
auto input = static_cast<Input *>(token);
1057+
return input->data.size() - input->consumed >= int(len);
1058+
};
1059+
ops.read_bytes = [](void *token, void *dst, size_t offset, size_t len) {
1060+
auto input = static_cast<Input *>(token);
1061+
return memcpy(dst, input->data.constData() + input->consumed + offset, len);
1062+
};
1063+
ops.advance_bytes = [](void *token, size_t len) {
1064+
auto input = static_cast<Input *>(token);
1065+
input->consumed += int(len);
1066+
};
1067+
ops.transfer_string = [](void *token, const void **userptr, size_t offset, size_t len) {
1068+
// ###
1069+
auto input = static_cast<Input *>(token);
1070+
if (input->data.size() - input->consumed < int(len + offset))
1071+
return CborErrorUnexpectedEOF;
1072+
input->consumed += int(offset);
1073+
*userptr = input->data.constData() + input->consumed;
1074+
input->consumed += int(len);
1075+
return CborNoError;
1076+
};
1077+
1078+
CborParser parser;
1079+
CborValue first;
1080+
CborError err = cbor_parser_init_reader(&ops, &parser, &first, &input);
1081+
QCOMPARE(err, CborNoError);
1082+
1083+
QString decoded;
1084+
err = parseOne(&first, &decoded);
1085+
QCOMPARE(err, CborNoError);
1086+
QCOMPARE(decoded, expected);
1087+
1088+
// check we consumed everything
1089+
QCOMPARE(input.consumed, data.size());
1090+
}
1091+
10411092
void tst_Parser::chunkedString_data()
10421093
{
10431094
QTest::addColumn<QByteArray>("data");

0 commit comments

Comments
 (0)