intel/tinycbor

Add cbor_encode_cbor

mkm85 opened this issue · 9 comments

mkm85 commented

I propose adding a function which can encode encoded cbor into a CborEncoder.

Lets assume you have a system where chunks of data is encoded as cbor. If you are going to merge these chunks into a new cbor map they have to be decoded and then encoded again into the map. If instead one can just make a map and insert the binary cbor directly into this map then a decoding and reencoding is unneccessary.

CborError cbor_encode_cbor(CborEncoder* encoder, uint8_t* cbor, size_t size);

Regards, Michael

The problem will be that we still need to decode and re-encode in order to keep the state in CborEncoder: it tries to keep the count of elements added, so it can tell you if you made a mistake.

Copying without decoding would be only possible if the array or map has unknown length. Would that be acceptable?

mkm85 commented

I was thinking about adding the cbor as a single value e.g.

uint8_t example1[5] = { 0x82, 0x01, 0x82, 0x02, 0x03 };
uint8_t example2[8] = { 0x82, 0x61, 0x61, 0xa1, 0x61, 0x62, 60x1, 0x63 };

CborEncoder encoder;
cbor_encoder_init(&encoder, ....)
CborEncoder map;
cbor_encoder_create_map(&encoder, &map, CborIndefiniteLength);
cbor_encode_text_stringz(&map, "foo");
cbor_encode_cbor(&map, example1, 5);
cbor_encode_text_stringz(&map, "bar");
cbor_encode_cbor(&map, example2, 8);
cbor_encoder_close_container(...)
...

I see that it will give some problems if the encoded cbor is containing multiple values.
And it would indeed be dangerous if an odd number of elements is added to a map. Maybe I is too dangerous to add such a functionality to the api.

That we could easily do, yes.

There is a tag for a Cbor byte string.
(from RFC 7049)
2.4.4.1. Encoded CBOR Data Item

Sometimes it is beneficial to carry an embedded CBOR data item that
is not meant to be decoded immediately at the time the enclosing data
item is being parsed. Tag 24 (CBOR data item) can be used to tag the
embedded byte string as a data item encoded in CBOR format.

If we do use cbor encode cbor that tag should be used.

mkm85 commented

Using a tag and a byte string is possible with the current api, but it was not the intended semantics with this request. So the name is probably not the best.

Right, something like cbor_encode_encoded_item.

I'll like to vote for this feature. I have two use cases where i have a hard coded cbor map or array that i would like to put into a high level map. E.g., i have a framework that receives a request to query for capabilities. An application on top of this layers defines the capabilities array.

/*
 * capabilities = {"modes": ["fly", "walk", "run"], "other": { ... }}
 * User defined function for getting capabilities.
 */
void get_capabilities(const uint8_t **ptr, size_t *size)
{
    /* Could be hard coded or created at startup for instance. */
    static uint8_t capabilities[] = { ... };
}


/* On a framework level. */
write_capabilities_response(CborEncoder *response)
{
   /* Here some other information are written to response that the framework requires. */
   .....

    /* Write user specified capabilities. */
    const uint8_t *capabilities = NULL;
    size_t size = 0;
    get_capabilities(&capabilities, &size);
    verify(capabilities != NULL and size > 0)
    cbor_encode_text_stringz(&response, "capabilities");
    cbor_encode_cbor(&response, capabilities, size);
}

I realize this was quite a bad example. But i have maps and arrays defined elsewhere and want to write them into the encoder.

Hello. I tried to use TSonono solution and it gave me an error. What I am trying to do is. I want to add cbor tagged data into array. Please see my below code. It gave me an error when i try to close the container.

else if ([object isKindOfClass:NSData.class]) {
        NSData *dataObject = (NSData *)object;
     
        CborParser parser;
        CborValue value;
        const int flags = 0;
        
        uint8_t *inBuffer = (uint8_t *)dataObject.bytes;
        size_t inBufferLen = dataObject.length;
        
        CborError err = cbor_parser_init(inBuffer, inBufferLen, 0, &parser, &value);
        CborType i = cbor_value_get_type(&value);
        
        
        if(i == CborTagType) {
            return [self ds_performSafeEncodingIntoBuffer:buffer bufferSize:bufferSize encoder:encoder encodingBlock:^CborError {
                return cbor_encode_raw(encoder,  inBuffer, inBufferLen);
            }];
        }

    }

This is the code for cbor_encode_raw
CborError cbor_encode_raw(CborEncoder *encoder, const uint8_t *raw, size_t length) { return append_to_buffer(encoder, raw, length); }

Your code is not correct, unless you're certain that the buffer you got contains exactly one item. What error did you get?