Skip to content

Commit

Permalink
Improve encode documentation (#65)
Browse files Browse the repository at this point in the history
Improve the documentation for QCBOREncode_Init() and QCBOREncode_Finish().

replace all use of the deprecated macro MakeUsefulBufOnStack().

Address #62 

Co-authored-by: Laurence Lundblade <lgl@securitytheory.com>
  • Loading branch information
laurencelundblade and Laurence Lundblade committed Dec 1, 2020
1 parent 9961530 commit 8510f8c
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 43 deletions.
34 changes: 26 additions & 8 deletions example.c
Original file line number Diff line number Diff line change
Expand Up @@ -659,14 +659,32 @@ EngineDecodeErrors DecodeEngineBasic(UsefulBufC EncodedEngine, CarEngine *pE)

int32_t RunQCborExample()
{
CarEngine E, DecodedEngine;
MakeUsefulBufOnStack( EngineBuffer, 300);
UsefulBufC EncodedEngine;

MakeUsefulBufOnStack( InDefEngineBuffer, 300);
UsefulBufC InDefEncodedEngine;

EngineDecodeErrors uErr;
CarEngine E, DecodedEngine;

/* For every buffer used by QCBOR a pointer and a length are always
* carried in a UsefulBuf. This is a secure coding and hygene
* practice to help make sure code never runs off the end of a buffer.
*
* UsefulBuf structures are passed as a stack parameter to make the
* code prettier. The object code generated isn't much different from
* passing a pointer parameter and a length parameter.
*
* This macro is equivalent to:
* uint8_t __pBufEngineBuffer[300];
* UsefulBuf EngineBuffer = {__pBufEngineBuffer, 300};
*/
UsefulBuf_MAKE_STACK_UB( EngineBuffer, 300);

/* The pointer in UsefulBuf is not const and used for representing
* a buffer to be written to. For UsefulbufC, the pointer is const
* and is used to represent a buffer that has been written to.
*/
UsefulBufC EncodedEngine;

UsefulBuf_MAKE_STACK_UB( InDefEngineBuffer, 300);
UsefulBufC InDefEncodedEngine;

EngineDecodeErrors uErr;

EngineInit(&E);

Expand Down
92 changes: 62 additions & 30 deletions inc/qcbor/qcbor_encode.h
Original file line number Diff line number Diff line change
Expand Up @@ -407,30 +407,56 @@ typedef struct _QCBOREncodeContext QCBOREncodeContext;
Initialize the encoder to prepare to encode some CBOR.
@param[in,out] pCtx The encoder context to initialize.
@param[in] Storage The buffer into which this encoded result
will be placed.
Call this once at the start of an encoding of a CBOR structure. Then
call the various @c QCBOREncode_AddXxx() functions to add the data
items. Then call QCBOREncode_Finish().
The maximum output buffer is @c UINT32_MAX (4GB). This is not a
practical limit in any way and reduces the memory needed by the
implementation. The error @ref QCBOR_ERR_BUFFER_TOO_LARGE will be
returned by QCBOREncode_Finish() if a larger buffer length is passed
in.
If this is called with @c Storage.ptr as @c NULL and @c Storage.len a
large value like @c UINT32_MAX, all the QCBOREncode_AddXxx()
functions and QCBOREncode_Finish() can still be called. No data will
be encoded, but the length of what would be encoded will be
calculated. The length of the encoded structure will be handed back
in the call to QCBOREncode_Finish(). You can then allocate a buffer
of that size and call all the encoding again, this time to fill in
the buffer.
@param[in] Storage The buffer into which the encoded result
will be written.
Call this once at the start of an encoding of some CBOR. Then call
the many functions like QCBOREncode_AddInt64() and
QCBOREncode_AddText() to add the different data items. Finally, call
QCBOREncode_Finish() to get the pointer and length of the encoded
result.
The primary purpose of this function is to give the pointer and
length of the output buffer into which the encoded CBOR will be
written. This is done with a @ref UsefulBuf structure, which is just
a pointer and length (it is equivalent to two parameters, one a
pointer and one a length, but a little prettier).
The output buffer can be allocated any way (malloc, stack,
static). It is just some memory that QCBOR writes to. The length must
be the length of the allocated buffer. QCBOR will never write past
that length, but might write up to that length. If the buffer is too
small, encoding will go into an error state and not write anything
further.
If allocating on the stack the convenience macro
UsefulBuf_MAKE_STACK_UB() can be used, but its use is not required.
Since there is no reallocation or such, the output buffer must be
correctly sized when passed in here. It is OK, but wasteful if it is
too large. One way to pick the size is to figure out the maximum size
that will ever be needed and hard code a buffer of that size.
Another way to do it is to have QCBOR calculate it for you. To do
this set @c Storage.ptr to @c NULL and @c Storage.len to @c
UINT32_MAX. Then call all the functions to add the CBOR exactly as if
encoding for real. Then call QCBOREncode_Finish(). The pointer
returned will be @c NULL, but the length returned is that of what would
be encoded. Once the length is obtained, allocate a buffer of that
size, call QCBOREncode_Init() again with the real buffer. Call all
the add functions again and finally, QCBOREncode_Finish() to obtain
the final result. This uses almost twice the CPU time, but that is
usually not an issue.
See QCBOREncode_Finish() for how the pointer and length for the
encoded CBOR is returned.
The maximum output buffer size allowed is @c UINT32_MAX (4GB). The
error @ref QCBOR_ERR_BUFFER_TOO_LARGE will be returned by
QCBOREncode_Finish() if a larger buffer length is passed in.
A @ref QCBOREncodeContext can be reused over and over as long as
QCBOREncode_Init() is called.
QCBOREncode_Init() is called before each use.
*/
void QCBOREncode_Init(QCBOREncodeContext *pCtx, UsefulBuf Storage);

Expand Down Expand Up @@ -1690,7 +1716,8 @@ static void QCBOREncode_AddEncodedToMapN(QCBOREncodeContext *pCtx, int64_t nLabe
@brief Get the encoded result.
@param[in] pCtx The context to finish encoding with.
@param[out] pEncodedCBOR Pointer and length of encoded CBOR.
@param[out] pEncodedCBOR Structure in which the pointer and length of the encoded
CBOR is returned.
@retval QCBOR_ERR_TOO_MANY_CLOSES Nesting error
Expand All @@ -1706,12 +1733,17 @@ static void QCBOREncode_AddEncodedToMapN(QCBOREncodeContext *pCtx, int64_t nLabe
@retval QCBOR_ERR_ARRAY_TOO_LONG Implementation limit
If this returns success @ref QCBOR_SUCCESS the encoding was a success
and the return length is correct and complete.
On success, the pointer and length of the encoded CBOR are returned
in @c *pEncodedCBOR. The pointer is the same pointer that was passed
in to QCBOREncode_Init(). Note that it is not const when passed to
QCBOREncode_Init(), but it is const when returned here. The length
will be smaller than or equal to the length passed in when
QCBOREncode_Init() as this is the length of the actual result, not
the size of the buffer it was written to.
If no buffer was passed to QCBOREncode_Init(), then only the length
was computed. If a buffer was passed, then the encoded CBOR is in the
buffer.
If a @c NULL was passed for @c Storage.ptr when QCBOREncode_Init()
was called, @c NULL will be returned here, but the length will be
that of the CBOR that would have been encoded.
Encoding errors primarily manifest here as most other encoding function
do no return an error. They just set the error state in the encode
Expand Down Expand Up @@ -1743,8 +1775,8 @@ static void QCBOREncode_AddEncodedToMapN(QCBOREncodeContext *pCtx, int64_t nLabe
can also be interleaved with calls to QCBOREncode_FinishGetSize().
QCBOREncode_GetErrorState() can be called to get the current
error state and abort encoding early as an optimization, but is
is never required.
error state in order to abort encoding early as an optimization, but
calling it is is never required.
*/
QCBORError QCBOREncode_Finish(QCBOREncodeContext *pCtx, UsefulBufC *pEncodedCBOR);

Expand Down
6 changes: 3 additions & 3 deletions test/qcbor_decode_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -2113,7 +2113,7 @@ int32_t DecodeFailureTests()
through the use of SIZE_MAX.
*/

MakeUsefulBufOnStack( HeadBuf, QCBOR_HEAD_BUFFER_SIZE);
UsefulBuf_MAKE_STACK_UB( HeadBuf, QCBOR_HEAD_BUFFER_SIZE);
UsefulBufC EncodedHead;

// This makes a CBOR head with a text string that is very long
Expand Down Expand Up @@ -6317,7 +6317,7 @@ static UsefulBufC EncodeBstrWrapTestData(UsefulBuf OutputBuffer)

int32_t EnterBstrTest()
{
MakeUsefulBufOnStack(OutputBuffer, 100);
UsefulBuf_MAKE_STACK_UB(OutputBuffer, 100);

QCBORDecodeContext DC;

Expand Down Expand Up @@ -6752,7 +6752,7 @@ int32_t SpiffyIndefiniteLengthStringsTests()
UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spMapWithIndefLenStrings),
QCBOR_DECODE_MODE_NORMAL);

MakeUsefulBufOnStack(StringBuf, 200);
UsefulBuf_MAKE_STACK_UB(StringBuf, 200);
QCBORDecode_SetMemPool(&DCtx, StringBuf, false);

UsefulBufC ByteString;
Expand Down
4 changes: 2 additions & 2 deletions test/qcbor_encode_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -2716,7 +2716,7 @@ int32_t QCBORHeadTest()
* other test exercises QCBOREncode_EncodeHead().
*/
// ---- basic test to encode a zero ----
MakeUsefulBufOnStack(RightSize, QCBOR_HEAD_BUFFER_SIZE);
UsefulBuf_MAKE_STACK_UB(RightSize, QCBOR_HEAD_BUFFER_SIZE);

UsefulBufC encoded = QCBOREncode_EncodeHead(RightSize,
CBOR_MAJOR_TYPE_POSITIVE_INT,
Expand Down Expand Up @@ -2744,7 +2744,7 @@ int32_t QCBORHeadTest()


// ---- Try to encode into too-small a buffer ----
MakeUsefulBufOnStack(TooSmall, QCBOR_HEAD_BUFFER_SIZE-1);
UsefulBuf_MAKE_STACK_UB(TooSmall, QCBOR_HEAD_BUFFER_SIZE-1);

encoded = QCBOREncode_EncodeHead(TooSmall,
CBOR_MAJOR_TYPE_POSITIVE_INT,
Expand Down

0 comments on commit 8510f8c

Please sign in to comment.