LCOV - code coverage report
Current view: top level - src - thingset_bin.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 363 504 72.0 %
Date: 2025-02-03 11:19:13 Functions: 31 33 93.9 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 210 355 59.2 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) The ThingSet Project Contributors
       3                 :            :  *
       4                 :            :  * SPDX-License-Identifier: Apache-2.0
       5                 :            :  */
       6                 :            : 
       7                 :            : #include <thingset.h>
       8                 :            : 
       9                 :            : #include "thingset_internal.h"
      10                 :            : 
      11                 :            : #include <zcbor_common.h>
      12                 :            : #include <zcbor_decode.h>
      13                 :            : #include <zcbor_encode.h>
      14                 :            : 
      15                 :            : #include <math.h>
      16                 :            : #include <stdarg.h>
      17                 :            : #include <stdio.h>
      18                 :            : #include <string.h>
      19                 :            : 
      20                 :        150 : static void bin_decoder_init(struct thingset_context *ts, const uint8_t *payload,
      21                 :            :                              size_t payload_len)
      22                 :            : {
      23                 :        150 :     zcbor_new_decode_state(ts->decoder, ZCBOR_ARRAY_SIZE(ts->decoder), payload, payload_len, 1,
      24                 :            :                            NULL, 0);
      25                 :            : 
      26                 :            :     /* required to accept incoming data which does not use the most compact encoding */
      27                 :        150 :     ts->decoder->constant_state->enforce_canonical = false;
      28                 :        150 : }
      29                 :            : 
      30                 :         19 : static int bin_serialize_map_start(struct thingset_context *ts)
      31                 :            : {
      32         [ +  - ]:         19 :     return zcbor_map_start_encode(ts->encoder, UINT8_MAX) ? 0 : -THINGSET_ERR_RESPONSE_TOO_LARGE;
      33                 :            : }
      34                 :            : 
      35                 :         19 : static int bin_serialize_map_end(struct thingset_context *ts)
      36                 :            : {
      37         [ +  - ]:         19 :     return zcbor_map_end_encode(ts->encoder, UINT8_MAX) ? 0 : -THINGSET_ERR_RESPONSE_TOO_LARGE;
      38                 :            : }
      39                 :            : 
      40                 :         20 : static int bin_serialize_list_start(struct thingset_context *ts)
      41                 :            : {
      42         [ +  - ]:         20 :     return zcbor_list_start_encode(ts->encoder, UINT8_MAX) ? 0 : -THINGSET_ERR_RESPONSE_TOO_LARGE;
      43                 :            : }
      44                 :            : 
      45                 :         17 : static int bin_serialize_list_end(struct thingset_context *ts)
      46                 :            : {
      47         [ +  - ]:         17 :     return zcbor_list_end_encode(ts->encoder, UINT8_MAX) ? 0 : -THINGSET_ERR_RESPONSE_TOO_LARGE;
      48                 :            : }
      49                 :            : 
      50                 :        129 : int bin_serialize_response(struct thingset_context *ts, uint8_t code, const char *msg, ...)
      51                 :            : {
      52                 :            :     va_list vargs;
      53                 :            : 
      54                 :        129 :     ts->rsp[0] = code;
      55                 :            : 
      56                 :        129 :     zcbor_update_state(ts->encoder, ts->rsp + 1, ts->rsp_size - 1);
      57                 :        129 :     zcbor_nil_put(ts->encoder, NULL);
      58                 :            : 
      59         [ +  + ]:        129 :     if (THINGSET_ERROR(code)) {
      60         [ +  + ]:         14 :         if (msg != NULL) {
      61                 :            :             /* zcbor uses memmove internally, so we can use the encoder buffer with an
      62                 :            :              * offset for the string header for temporary storage of the message
      63                 :            :              */
      64                 :          9 :             uint8_t *msg_buf_start = ts->encoder->payload_mut + 2;
      65                 :          9 :             size_t msg_buf_size = ts->encoder->payload_end - msg_buf_start;
      66                 :            : 
      67                 :          9 :             va_start(vargs, msg);
      68                 :          9 :             int ret = vsnprintf((char *)msg_buf_start, msg_buf_size, msg, vargs);
      69                 :          9 :             va_end(vargs);
      70                 :            : 
      71   [ +  -  +  - ]:          9 :             if (ret >= 0 && ret < msg_buf_size) {
      72                 :          9 :                 zcbor_tstr_encode_ptr(ts->encoder, msg_buf_start, ret);
      73                 :            :             }
      74                 :            :         }
      75                 :            :     }
      76                 :            : 
      77                 :        129 :     return 0;
      78                 :            : }
      79                 :            : 
      80                 :            : /**
      81                 :            :  * @returns 0 or negative ThingSet reponse code in case of error
      82                 :            :  */
      83                 :        164 : static int bin_serialize_simple_value(zcbor_state_t *encoder, union thingset_data_pointer data,
      84                 :            :                                       int type, int detail)
      85                 :            : {
      86                 :        164 :     bool success = true;
      87                 :            : 
      88   [ +  +  +  +  :        164 :     switch (type) {
          +  +  +  +  +  
             +  +  +  -  
                      + ]
      89                 :            : #if CONFIG_THINGSET_64BIT_TYPES_SUPPORT
      90                 :          4 :         case THINGSET_TYPE_U64:
      91                 :          4 :             success = zcbor_uint64_put(encoder, *data.u64);
      92                 :          4 :             break;
      93                 :          4 :         case THINGSET_TYPE_I64:
      94                 :          4 :             success = zcbor_int64_put(encoder, *data.i64);
      95                 :          4 :             break;
      96                 :            : #endif
      97                 :         17 :         case THINGSET_TYPE_U32:
      98                 :         17 :             success = zcbor_uint32_put(encoder, *data.u32);
      99                 :         17 :             break;
     100                 :         20 :         case THINGSET_TYPE_I32:
     101                 :         20 :             success = zcbor_int32_put(encoder, *data.i32);
     102                 :         20 :             break;
     103                 :          4 :         case THINGSET_TYPE_U16:
     104                 :          4 :             success = zcbor_uint32_put(encoder, *data.u16);
     105                 :          4 :             break;
     106                 :          4 :         case THINGSET_TYPE_I16:
     107                 :          4 :             success = zcbor_int32_put(encoder, *data.i16);
     108                 :          4 :             break;
     109                 :          4 :         case THINGSET_TYPE_U8:
     110                 :          4 :             success = zcbor_uint32_put(encoder, *data.u8);
     111                 :          4 :             break;
     112                 :          4 :         case THINGSET_TYPE_I8:
     113                 :          4 :             success = zcbor_int32_put(encoder, *data.i8);
     114                 :          4 :             break;
     115                 :         35 :         case THINGSET_TYPE_F32:
     116         [ -  + ]:         35 :             if (IS_ENABLED(CONFIG_THINGSET_ENCODE_ZERO_DECIMAL_FLOATS_AS_INTEGERS) && detail == 0)
     117                 :            :             { /* round to 0 decimals: use int */
     118                 :          0 :                 success = zcbor_int32_put(encoder, lroundf(*data.f32));
     119                 :            :             }
     120                 :            :             else {
     121                 :         35 :                 success = zcbor_float32_put(encoder, *data.f32);
     122                 :            :             }
     123                 :         35 :             break;
     124                 :            : #if CONFIG_THINGSET_DECFRAC_TYPE_SUPPORT
     125                 :          4 :         case THINGSET_TYPE_DECFRAC:
     126                 :          4 :             success = zcbor_tag_put(encoder, ZCBOR_TAG_DECFRAC_ARR);
     127   [ +  -  +  - ]:          4 :             success = success && zcbor_list_start_encode(encoder, 2);
     128   [ +  -  +  - ]:          4 :             success = success && zcbor_int32_put(encoder, -detail);       /* exponent */
     129   [ +  -  +  - ]:          4 :             success = success && zcbor_int32_put(encoder, *data.decfrac); /* mantissa */
     130   [ +  -  +  - ]:          4 :             success = success && zcbor_list_end_encode(encoder, 2);
     131                 :          4 :             break;
     132                 :            : #endif
     133                 :          9 :         case THINGSET_TYPE_BOOL:
     134                 :          9 :             success = zcbor_bool_put(encoder, *data.b);
     135                 :          9 :             break;
     136                 :          8 :         case THINGSET_TYPE_STRING:
     137                 :          8 :             success = zcbor_tstr_put_term(encoder, data.str, detail);
     138                 :          8 :             break;
     139                 :            : #if CONFIG_THINGSET_BYTES_TYPE_SUPPORT
     140                 :          0 :         case THINGSET_TYPE_BYTES:
     141                 :          0 :             success = zcbor_bstr_encode_ptr(encoder, data.bytes->bytes, data.bytes->num_bytes);
     142                 :          0 :             break;
     143                 :            : #endif
     144                 :         47 :         default:
     145                 :         47 :             return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     146                 :            :     }
     147                 :            : 
     148         [ +  + ]:        117 :     if (success) {
     149                 :        116 :         return 0;
     150                 :            :     }
     151                 :            :     else {
     152                 :          1 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     153                 :            :     }
     154                 :            : }
     155                 :            : 
     156                 :         23 : static int bin_serialize_path(struct thingset_context *ts,
     157                 :            :                               const struct thingset_data_object *object)
     158                 :            : {
     159                 :            :     /* zcbor uses memmove internally, so we can use the encoder buffer with an
     160                 :            :      * offset for the string header for temporary storage of the path
     161                 :            :      */
     162                 :         23 :     uint8_t *buf_path_start = ts->encoder->payload_mut + 2;
     163                 :         23 :     size_t buf_path_size = ts->encoder->payload_end - buf_path_start;
     164                 :            : 
     165                 :         23 :     int path_len = thingset_get_path(ts, (char *)buf_path_start, buf_path_size, object);
     166         [ -  + ]:         23 :     if (path_len < 0) {
     167                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     168                 :            :     }
     169                 :            : 
     170                 :         23 :     return zcbor_tstr_encode_ptr(ts->encoder, buf_path_start, path_len)
     171                 :            :                ? 0
     172         [ +  - ]:         23 :                : -THINGSET_ERR_RESPONSE_TOO_LARGE;
     173                 :            : }
     174                 :            : 
     175                 :            : #ifdef CONFIG_THINGSET_METADATA_ENDPOINT
     176                 :          7 : static int bin_serialize_metadata(struct thingset_context *ts,
     177                 :            :                                   const struct thingset_data_object *object)
     178                 :            : {
     179                 :          7 :     int err = bin_serialize_map_start(ts);
     180         [ -  + ]:          7 :     if (err) {
     181                 :          0 :         return err;
     182                 :            :     }
     183                 :            : 
     184                 :          7 :     const char name[] = "name";
     185         [ -  + ]:          7 :     if (!zcbor_tstr_put_lit(ts->encoder, name)) {
     186                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     187                 :            :     }
     188                 :            : 
     189         [ -  + ]:          7 :     if (!zcbor_tstr_encode_ptr(ts->encoder, object->name, strlen(object->name))) {
     190                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     191                 :            :     }
     192                 :            : 
     193                 :          7 :     const char type[] = "type";
     194         [ -  + ]:          7 :     if (!zcbor_tstr_put_lit(ts->encoder, type)) {
     195                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     196                 :            :     }
     197                 :            : 
     198                 :            :     char buf[128];
     199                 :          7 :     int len = thingset_get_type_name(ts, object, buf, sizeof(buf));
     200         [ -  + ]:          7 :     if (len < 0) {
     201                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     202                 :            :     }
     203         [ -  + ]:          7 :     if (!zcbor_tstr_encode_ptr(ts->encoder, buf, len)) {
     204                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     205                 :            :     }
     206                 :            : 
     207         [ -  + ]:          7 :     if ((err = bin_serialize_map_end(ts))) {
     208                 :          0 :         return err;
     209                 :            :     }
     210                 :            : 
     211                 :          7 :     return 0;
     212                 :            : }
     213                 :            : #endif /* CONFIG_THINGSET_METADATA_ENDPOINT */
     214                 :            : 
     215                 :        146 : static int bin_serialize_value(struct thingset_context *ts,
     216                 :            :                                const struct thingset_data_object *object)
     217                 :            : {
     218                 :        146 :     bool success = false;
     219                 :            :     int err;
     220                 :            : 
     221                 :        146 :     err = bin_serialize_simple_value(ts->encoder, object->data, object->type, object->detail);
     222         [ +  + ]:        146 :     if (err != -THINGSET_ERR_UNSUPPORTED_FORMAT) {
     223                 :         99 :         return err;
     224                 :            :     }
     225                 :            : 
     226                 :            :     /* not a simple value */
     227         [ +  + ]:         47 :     if (object->type == THINGSET_TYPE_GROUP) {
     228                 :         14 :         success = zcbor_nil_put(ts->encoder, NULL);
     229                 :            :     }
     230         [ +  + ]:         33 :     else if (object->type == THINGSET_TYPE_RECORDS) {
     231                 :            :         if (IS_ENABLED(CONFIG_THINGSET_REPORT_RECORD_SERIALIZATION)
     232                 :            :             && ts->rsp[0] == THINGSET_BIN_REPORT)
     233                 :            :         {
     234                 :            :             /* serialise all records */
     235                 :            :             success = zcbor_list_start_encode(ts->encoder, UINT8_MAX);
     236                 :            :             for (unsigned int i = 0; i < object->data.records->num_records; i++) {
     237                 :            :                 err = thingset_common_serialize_record(ts, object, i);
     238                 :            :             }
     239                 :            :             success = success && zcbor_list_end_encode(ts->encoder, UINT8_MAX);
     240                 :            :         }
     241                 :            :         else {
     242                 :         14 :             success = zcbor_uint32_put(ts->encoder, object->data.records->num_records);
     243                 :            :         }
     244                 :            :     }
     245   [ +  +  +  + ]:         19 :     else if (object->type == THINGSET_TYPE_FN_VOID || object->type == THINGSET_TYPE_FN_I32) {
     246                 :          8 :         success = zcbor_list_start_encode(ts->encoder, UINT8_MAX);
     247         [ +  + ]:        576 :         for (unsigned int i = 0; i < ts->num_objects; i++) {
     248         [ +  + ]:        568 :             if (ts->data_objects[i].parent_id == object->id) {
     249                 :          6 :                 zcbor_tstr_encode_ptr(ts->encoder, ts->data_objects[i].name,
     250                 :          6 :                                       strlen(ts->data_objects[i].name));
     251                 :            :             }
     252                 :            :         }
     253   [ +  -  +  - ]:          8 :         success = success && zcbor_list_end_encode(ts->encoder, UINT8_MAX);
     254                 :            :     }
     255         [ +  + ]:         11 :     else if (object->type == THINGSET_TYPE_SUBSET) {
     256                 :          5 :         success = zcbor_list_start_encode(ts->encoder, UINT8_MAX);
     257         [ +  + ]:        360 :         for (unsigned int i = 0; i < ts->num_objects; i++) {
     258         [ +  + ]:        355 :             if (ts->data_objects[i].subsets & object->data.subset) {
     259         [ +  + ]:         24 :                 if (ts->endpoint.use_ids) {
     260   [ +  -  +  - ]:          5 :                     success = success && zcbor_uint32_put(ts->encoder, ts->data_objects[i].id);
     261                 :            :                 }
     262                 :            :                 else {
     263   [ +  -  +  - ]:         19 :                     success = success && (bin_serialize_path(ts, &ts->data_objects[i]) == 0);
     264                 :            :                 }
     265                 :            :             }
     266                 :            :         }
     267   [ +  -  +  - ]:          5 :         success = success && zcbor_list_end_encode(ts->encoder, UINT8_MAX);
     268                 :            :     }
     269         [ +  - ]:          6 :     else if (object->type == THINGSET_TYPE_ARRAY) {
     270                 :          6 :         struct thingset_array *array = object->data.array;
     271                 :            : 
     272                 :          6 :         success = zcbor_list_start_encode(ts->encoder, array->num_elements);
     273                 :            : 
     274                 :          6 :         size_t type_size = thingset_type_size(array->element_type);
     275         [ +  + ]:         24 :         for (int i = 0; i < array->num_elements; i++) {
     276                 :            :             /* using uint8_t pointer for byte-wise pointer arithmetics */
     277                 :         18 :             union thingset_data_pointer data = { .u8 = array->elements.u8 + i * type_size };
     278                 :            :             err =
     279                 :         18 :                 bin_serialize_simple_value(ts->encoder, data, array->element_type, array->decimals);
     280         [ -  + ]:         18 :             if (err != 0) {
     281                 :            :                 /* finish up to leave encoder in defined state */
     282                 :          0 :                 zcbor_list_end_encode(ts->encoder, array->num_elements);
     283                 :          0 :                 return err;
     284                 :            :             }
     285                 :            :         }
     286                 :            : 
     287   [ +  -  +  - ]:          6 :         success = success && zcbor_list_end_encode(ts->encoder, array->num_elements);
     288                 :            :     }
     289                 :            :     else {
     290                 :          0 :         return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     291                 :            :     }
     292                 :            : 
     293         [ +  - ]:         47 :     if (success) {
     294                 :         47 :         return 0;
     295                 :            :     }
     296                 :            :     else {
     297                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     298                 :            :     }
     299                 :            : }
     300                 :            : 
     301                 :        154 : static int bin_serialize_key(struct thingset_context *ts, const struct thingset_data_object *object)
     302                 :            : {
     303         [ +  + ]:        154 :     if (ts->endpoint.use_ids) {
     304         [ -  + ]:         90 :         if (zcbor_uint32_put(ts->encoder, object->id) == false) {
     305                 :          0 :             return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     306                 :            :         }
     307                 :            :     }
     308                 :            :     else {
     309         [ -  + ]:         64 :         if (zcbor_tstr_encode_ptr(ts->encoder, object->name, strlen(object->name)) == false) {
     310                 :          0 :             return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     311                 :            :         }
     312                 :            :     }
     313                 :            : 
     314                 :        154 :     return 0;
     315                 :            : }
     316                 :            : 
     317                 :        124 : static int bin_serialize_key_value(struct thingset_context *ts,
     318                 :            :                                    const struct thingset_data_object *object)
     319                 :            : {
     320                 :        124 :     int err = ts->api->serialize_key(ts, object);
     321         [ -  + ]:        124 :     if (err != 0) {
     322                 :          0 :         return err;
     323                 :            :     }
     324                 :            : 
     325                 :        124 :     return ts->api->serialize_value(ts, object);
     326                 :            : }
     327                 :            : 
     328                 :         72 : static void bin_serialize_finish(struct thingset_context *ts)
     329                 :            : {
     330                 :         72 :     ts->rsp_pos = ts->encoder->payload - ts->rsp;
     331   [ +  +  +  + ]:         72 :     if (ts->rsp_pos == 2 && ts->rsp[1] == 0xF6) {
     332                 :            :         /* message with empty payload */
     333                 :         18 :         ts->rsp[ts->rsp_pos++] = 0xF6;
     334                 :            :     }
     335                 :         72 : }
     336                 :            : 
     337                 :            : /**
     338                 :            :  * Parse endpoint and fill response buffer with response in case of error.
     339                 :            :  *
     340                 :            :  * @returns 0 for success or negative ThingSet reponse code in case of error
     341                 :            :  */
     342                 :         64 : static int bin_parse_endpoint(struct thingset_context *ts)
     343                 :            : {
     344                 :            :     struct zcbor_string path;
     345                 :            :     uint32_t id;
     346                 :         64 :     int err = -THINGSET_ERR_NOT_FOUND;
     347                 :            : 
     348         [ +  + ]:         64 :     if (zcbor_tstr_decode(ts->decoder, &path) == true) {
     349                 :         21 :         err = thingset_endpoint_by_path(ts, &ts->endpoint, path.value, path.len);
     350                 :            :     }
     351   [ +  +  +  - ]:         43 :     else if (zcbor_uint32_decode(ts->decoder, &id) == true && id <= UINT16_MAX) {
     352                 :         42 :         err = thingset_endpoint_by_id(ts, &ts->endpoint, id);
     353                 :            :     }
     354         [ +  - ]:          1 :     else if (zcbor_list_start_decode(ts->decoder) == true) {
     355   [ +  -  +  - ]:          1 :         if (zcbor_uint32_decode(ts->decoder, &id) == true && id <= UINT16_MAX) {
     356                 :          1 :             err = thingset_endpoint_by_id(ts, &ts->endpoint, id);
     357         [ +  - ]:          1 :             if (err == 0) {
     358   [ +  -  +  - ]:          1 :                 if (!zcbor_int32_decode(ts->decoder, &ts->endpoint.index) || ts->endpoint.index < 0
     359         [ -  + ]:          1 :                     || !zcbor_list_end_decode(ts->decoder))
     360                 :            :                 {
     361                 :          0 :                     err = -THINGSET_ERR_BAD_REQUEST;
     362                 :            :                 }
     363                 :            :                 /* else: ID and index found, return 0 */
     364                 :            :             }
     365                 :            :         }
     366                 :            :     }
     367                 :            : 
     368         [ +  + ]:         64 :     if (err != 0) {
     369                 :          1 :         ts->api->serialize_response(ts, -err, "Invalid endpoint");
     370                 :          1 :         return err;
     371                 :            :     }
     372                 :            : 
     373                 :         63 :     ts->msg_payload = ts->decoder->payload;
     374                 :            : 
     375                 :            :     /* re-initialize decoder for payload parsing */
     376                 :         63 :     bin_decoder_init(ts, ts->msg_payload, ts->msg_len - (ts->msg_payload - ts->msg));
     377                 :            : 
     378                 :         63 :     return 0;
     379                 :            : }
     380                 :            : 
     381                 :          1 : int thingset_bin_desire(struct thingset_context *ts)
     382                 :            : {
     383                 :          1 :     return -THINGSET_ERR_NOT_IMPLEMENTED;
     384                 :            : }
     385                 :            : 
     386                 :          3 : int thingset_bin_export_subsets_progressively(struct thingset_context *ts, uint16_t subsets,
     387                 :            :                                               unsigned int *index, size_t *len)
     388                 :            : {
     389         [ +  + ]:          3 :     if (*index == 0) {
     390                 :          2 :         size_t num_elements = 0;
     391         [ +  + ]:        144 :         for (int i = 0; i < ts->num_objects; i++) {
     392         [ +  + ]:        142 :             if (ts->data_objects[i].subsets & subsets) {
     393                 :         10 :                 num_elements++;
     394                 :            :             }
     395                 :            :         }
     396                 :          2 :         zcbor_map_start_encode(ts->encoder, num_elements);
     397                 :            :     }
     398                 :            : 
     399         [ +  + ]:        145 :     while (*index < ts->num_objects) {
     400         [ +  + ]:        143 :         if (ts->data_objects[*index].subsets & subsets) {
     401                 :            :             /* update last length in case next serialisation runs out of room */
     402                 :         11 :             *len = ts->rsp_pos;
     403                 :         11 :             int ret = bin_serialize_key_value(ts, &ts->data_objects[*index]);
     404         [ +  + ]:         11 :             if (ret == -THINGSET_ERR_RESPONSE_TOO_LARGE) {
     405         [ +  - ]:          1 :                 if (ts->rsp_pos > 0) {
     406                 :            :                     /* reset pointer to position before we encoded this key-value
     407                 :            :                      * pair and ask for more data
     408                 :            :                      */
     409                 :          1 :                     ts->encoder->payload_mut = ts->rsp;
     410                 :          1 :                     ts->rsp_pos = 0;
     411                 :          1 :                     return 1;
     412                 :            :                 }
     413                 :            :                 else {
     414                 :            :                     /* this element alone is too large to fit the buffer */
     415                 :          0 :                     return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     416                 :            :                 }
     417                 :            :             }
     418         [ -  + ]:         10 :             else if (ret < 0) {
     419                 :          0 :                 return ret;
     420                 :            :             }
     421                 :            :         }
     422                 :        142 :         (*index)++;
     423                 :        142 :         ts->rsp_pos = ts->encoder->payload - ts->rsp;
     424                 :        142 :         *len = ts->rsp_pos;
     425                 :            :     }
     426                 :            : 
     427                 :            :     /* NB. there is no corresponding call to `zcbor_map_end_encode` because
     428                 :            :        we have already specified the exact number of elements in our call to
     429                 :            :        `zcbor_map_start_encode` above. */
     430                 :            : 
     431                 :          2 :     ts->api->serialize_finish(ts);
     432                 :          2 :     *len = ts->rsp_pos;
     433                 :          2 :     return 0;
     434                 :            : }
     435                 :            : 
     436                 :          2 : static int bin_serialize_subsets(struct thingset_context *ts, uint16_t subsets)
     437                 :            : {
     438                 :            :     bool success;
     439                 :            : 
     440                 :          2 :     success = zcbor_map_start_encode(ts->encoder, UINT8_MAX);
     441                 :            : 
     442         [ +  + ]:        144 :     for (unsigned int i = 0; i < ts->num_objects; i++) {
     443         [ +  + ]:        142 :         if (ts->data_objects[i].subsets & subsets) {
     444                 :         10 :             bin_serialize_key_value(ts, &ts->data_objects[i]);
     445                 :            :         }
     446                 :            :     }
     447                 :            : 
     448   [ +  -  +  - ]:          2 :     success = success && zcbor_map_end_encode(ts->encoder, UINT8_MAX);
     449                 :            : 
     450         [ +  - ]:          2 :     if (success) {
     451                 :          2 :         return 0;
     452                 :            :     }
     453                 :            :     else {
     454                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     455                 :            :     }
     456                 :            : }
     457                 :            : 
     458                 :          4 : static int bin_serialize_report_header(struct thingset_context *ts, const char *path)
     459                 :            : {
     460                 :            :     bool success;
     461                 :            : 
     462                 :          4 :     ts->rsp[0] = THINGSET_BIN_REPORT;
     463                 :            : 
     464         [ +  + ]:          4 :     if (ts->endpoint.use_ids) {
     465         [ +  + ]:          3 :         if (ts->endpoint.index == THINGSET_ENDPOINT_INDEX_NONE) {
     466                 :          2 :             success = zcbor_uint32_put(ts->encoder, ts->endpoint.object->id);
     467                 :            :         }
     468                 :            :         else {
     469                 :          1 :             success = zcbor_list_start_encode(ts->encoder, UINT8_MAX);
     470                 :          1 :             success |= zcbor_uint32_put(ts->encoder, ts->endpoint.object->id);
     471                 :          1 :             success |= zcbor_uint32_put(ts->encoder, ts->endpoint.index);
     472                 :          1 :             success |= zcbor_list_end_encode(ts->encoder, UINT8_MAX);
     473                 :            :         }
     474                 :            :     }
     475                 :            :     else {
     476                 :          1 :         success = zcbor_tstr_encode_ptr(ts->encoder, path, strlen(path));
     477                 :            :     }
     478                 :            : 
     479         [ +  - ]:          4 :     return success ? 0 : -THINGSET_ERR_RESPONSE_TOO_LARGE;
     480                 :            : }
     481                 :            : 
     482                 :          9 : static void bin_deserialize_payload_reset(struct thingset_context *ts)
     483                 :            : {
     484                 :          9 :     bin_decoder_init(ts, ts->msg_payload, ts->msg_len - (ts->msg_payload - ts->msg));
     485                 :          9 : }
     486                 :            : 
     487                 :          2 : static int bin_deserialize_string(struct thingset_context *ts, const char **str_start,
     488                 :            :                                   size_t *str_len)
     489                 :            : {
     490                 :            :     struct zcbor_string str;
     491         [ +  - ]:          2 :     if (zcbor_tstr_decode(ts->decoder, &str) == true) {
     492                 :          2 :         *str_start = str.value;
     493                 :          2 :         *str_len = str.len;
     494                 :          2 :         return 0;
     495                 :            :     }
     496                 :            :     else {
     497                 :          0 :         return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     498                 :            :     }
     499                 :            : }
     500                 :            : 
     501                 :         20 : static int bin_deserialize_null(struct thingset_context *ts)
     502                 :            : {
     503         [ +  + ]:         20 :     return zcbor_nil_expect(ts->decoder, NULL) ? 0 : -THINGSET_ERR_UNSUPPORTED_FORMAT;
     504                 :            : }
     505                 :            : 
     506                 :         71 : static int bin_deserialize_child(struct thingset_context *ts,
     507                 :            :                                  const struct thingset_data_object **object)
     508                 :            : {
     509                 :            :     struct zcbor_string name;
     510                 :            :     uint32_t id;
     511                 :            : 
     512   [ +  +  -  + ]:         71 :     if (ts->decoder->payload_end == ts->decoder->payload || ts->decoder->elem_count == 0) {
     513                 :         28 :         return -THINGSET_ERR_DESERIALIZATION_FINISHED;
     514                 :            :     }
     515                 :            : 
     516         [ +  + ]:         43 :     if (zcbor_tstr_decode(ts->decoder, &name) == true) {
     517                 :          9 :         *object = thingset_get_child_by_name(ts, ts->endpoint.object->id, name.value, name.len);
     518         [ +  + ]:          9 :         if (*object == NULL) {
     519                 :          2 :             return -THINGSET_ERR_NOT_FOUND;
     520                 :            :         }
     521                 :            :     }
     522   [ +  +  +  - ]:         34 :     else if (zcbor_uint32_decode(ts->decoder, &id) == true && id <= UINT16_MAX) {
     523                 :         33 :         *object = thingset_get_object_by_id(ts, id);
     524         [ -  + ]:         33 :         if (*object == NULL) {
     525                 :          0 :             return -THINGSET_ERR_NOT_FOUND;
     526                 :            :         }
     527         [ +  + ]:         33 :         else if (ts->endpoint.object->id != THINGSET_ID_PATHS
     528         [ +  + ]:         29 :                  && ts->endpoint.object->id != THINGSET_ID_METADATA
     529         [ +  + ]:         22 :                  && (*object)->parent_id != ts->endpoint.object->id)
     530                 :            :         {
     531                 :          1 :             return -THINGSET_ERR_BAD_REQUEST;
     532                 :            :         }
     533                 :            :     }
     534                 :            :     else {
     535                 :          1 :         return -THINGSET_ERR_BAD_REQUEST;
     536                 :            :     }
     537                 :            : 
     538                 :         39 :     return 0;
     539                 :            : }
     540                 :            : 
     541                 :         29 : static int bin_deserialize_list_start(struct thingset_context *ts)
     542                 :            : {
     543         [ +  + ]:         29 :     return zcbor_list_start_decode(ts->decoder) ? 0 : -THINGSET_ERR_UNSUPPORTED_FORMAT;
     544                 :            : }
     545                 :            : 
     546                 :         20 : static int bin_deserialize_map_start(struct thingset_context *ts)
     547                 :            : {
     548         [ +  - ]:         20 :     return zcbor_map_start_decode(ts->decoder) ? 0 : -THINGSET_ERR_UNSUPPORTED_FORMAT;
     549                 :            : }
     550                 :            : 
     551                 :         50 : static int bin_deserialize_simple_value(struct thingset_context *ts,
     552                 :            :                                         union thingset_data_pointer data, int type, int detail,
     553                 :            :                                         bool check_only)
     554                 :            : {
     555                 :            :     bool success;
     556                 :            : 
     557         [ +  + ]:         50 :     if (ts->decoder->payload_end == ts->decoder->payload) {
     558                 :          5 :         return -THINGSET_ERR_DESERIALIZATION_FINISHED;
     559                 :            :     }
     560                 :            : 
     561   [ -  -  +  +  :         45 :     switch (type) {
          -  -  -  -  +  
             -  +  +  +  
                      + ]
     562                 :            : #if CONFIG_THINGSET_64BIT_TYPES_SUPPORT
     563                 :          0 :         case THINGSET_TYPE_U64:
     564                 :          0 :             success = zcbor_uint64_decode(ts->decoder, data.u64);
     565                 :          0 :             break;
     566                 :          0 :         case THINGSET_TYPE_I64:
     567                 :          0 :             success = zcbor_int64_decode(ts->decoder, data.i64);
     568                 :          0 :             break;
     569                 :            : #endif
     570                 :          6 :         case THINGSET_TYPE_U32:
     571                 :          6 :             success = zcbor_uint32_decode(ts->decoder, data.u32);
     572                 :          6 :             break;
     573                 :          9 :         case THINGSET_TYPE_I32:
     574                 :          9 :             success = zcbor_int32_decode(ts->decoder, data.i32);
     575                 :          9 :             break;
     576                 :          0 :         case THINGSET_TYPE_U16:
     577                 :          0 :             success = zcbor_uint_decode(ts->decoder, data.u16, 2);
     578                 :          0 :             break;
     579                 :          0 :         case THINGSET_TYPE_I16:
     580                 :          0 :             success = zcbor_int_decode(ts->decoder, data.i16, 2);
     581                 :          0 :             break;
     582                 :          0 :         case THINGSET_TYPE_U8:
     583                 :          0 :             success = zcbor_uint_decode(ts->decoder, data.u8, 1);
     584                 :          0 :             break;
     585                 :          0 :         case THINGSET_TYPE_I8:
     586                 :          0 :             success = zcbor_int_decode(ts->decoder, data.i8, 1);
     587                 :          0 :             break;
     588                 :         14 :         case THINGSET_TYPE_F32:
     589                 :         14 :             success = zcbor_float16_32_decode(ts->decoder, data.f32);
     590         [ +  + ]:         14 :             if (!success) {
     591                 :            :                 /* try integer type */
     592                 :            :                 int32_t tmp;
     593         [ +  - ]:          5 :                 if (zcbor_int32_decode(ts->decoder, &tmp) == true) {
     594                 :          5 :                     *data.f32 = tmp;
     595                 :          5 :                     success = true;
     596                 :            :                 }
     597                 :            :             }
     598                 :         14 :             break;
     599                 :            : #if CONFIG_THINGSET_DECFRAC_TYPE_SUPPORT
     600                 :          0 :         case THINGSET_TYPE_DECFRAC: {
     601                 :          0 :             int32_t exponent = -detail;
     602                 :          0 :             int32_t *mantissa = data.decfrac;
     603         [ #  # ]:          0 :             if (zcbor_tag_expect(ts->decoder, ZCBOR_TAG_DECFRAC_ARR)) {
     604                 :          0 :                 success = zcbor_list_start_decode(ts->decoder);
     605                 :            :                 int32_t mantissa_tmp;
     606                 :            :                 int32_t exponent_received;
     607   [ #  #  #  # ]:          0 :                 success = success && zcbor_int32_decode(ts->decoder, &exponent_received);
     608   [ #  #  #  # ]:          0 :                 success = success && zcbor_int32_decode(ts->decoder, &mantissa_tmp);
     609                 :            : 
     610         [ #  # ]:          0 :                 for (int i = exponent_received; i < exponent; i++) {
     611                 :          0 :                     mantissa_tmp /= 10;
     612                 :            :                 }
     613         [ #  # ]:          0 :                 for (int i = exponent_received; i > exponent; i--) {
     614                 :          0 :                     mantissa_tmp *= 10;
     615                 :            :                 }
     616                 :          0 :                 *mantissa = mantissa_tmp;
     617                 :            :             }
     618                 :            :             else {
     619                 :            :                 /* try integer and float types */
     620                 :            :                 int32_t i32;
     621                 :            :                 float f32;
     622         [ #  # ]:          0 :                 if (zcbor_int32_decode(ts->decoder, &i32) == true) {
     623         [ #  # ]:          0 :                     for (int i = 0; i < exponent; i++) {
     624                 :          0 :                         i32 /= 10;
     625                 :            :                     }
     626         [ #  # ]:          0 :                     for (int i = 0; i > exponent; i--) {
     627                 :          0 :                         i32 *= 10;
     628                 :            :                     }
     629                 :          0 :                     *mantissa = i32;
     630                 :          0 :                     success = true;
     631                 :            :                 }
     632         [ #  # ]:          0 :                 else if (zcbor_float16_32_decode(ts->decoder, &f32) == true) {
     633         [ #  # ]:          0 :                     for (int i = 0; i < exponent; i++) {
     634                 :          0 :                         f32 /= 10.0F;
     635                 :            :                     }
     636         [ #  # ]:          0 :                     for (int i = 0; i > exponent; i--) {
     637                 :          0 :                         f32 *= 10.0F;
     638                 :            :                     }
     639                 :          0 :                     *mantissa = (int32_t)f32;
     640                 :          0 :                     success = true;
     641                 :            :                 }
     642                 :            :                 else {
     643                 :          0 :                     success = false;
     644                 :            :                 }
     645                 :            :             }
     646                 :          0 :             break;
     647                 :            :         }
     648                 :            : #endif
     649                 :          7 :         case THINGSET_TYPE_BOOL:
     650                 :          7 :             success = zcbor_bool_decode(ts->decoder, data.b);
     651                 :          7 :             break;
     652                 :          2 :         case THINGSET_TYPE_STRING: {
     653                 :            :             struct zcbor_string str;
     654                 :          2 :             success = zcbor_tstr_decode(ts->decoder, &str);
     655   [ +  -  +  - ]:          2 :             if (success && str.len < detail) {
     656         [ +  - ]:          2 :                 if (!check_only) {
     657                 :          2 :                     strncpy(data.str, str.value, str.len);
     658                 :          2 :                     data.str[str.len] = '\0';
     659                 :            :                 }
     660                 :            :             }
     661                 :            :             else {
     662                 :          0 :                 success = false;
     663                 :            :             }
     664                 :          2 :             break;
     665                 :            :         }
     666                 :            : #if CONFIG_THINGSET_BYTES_TYPE_SUPPORT
     667                 :          2 :         case THINGSET_TYPE_BYTES: {
     668                 :          2 :             struct thingset_bytes *bytes_buf = data.bytes;
     669                 :            :             struct zcbor_string bstr;
     670                 :          2 :             success = zcbor_bstr_decode(ts->decoder, &bstr);
     671   [ +  -  +  - ]:          2 :             if (success && bstr.len <= bytes_buf->max_bytes) {
     672         [ +  + ]:          2 :                 if (!check_only) {
     673                 :          1 :                     memcpy(bytes_buf->bytes, bstr.value, bstr.len);
     674                 :          1 :                     bytes_buf->num_bytes = bstr.len;
     675                 :            :                 }
     676                 :            :             }
     677                 :            :             else {
     678                 :          0 :                 success = false;
     679                 :            :             }
     680                 :          2 :             break;
     681                 :            :         }
     682                 :            : #endif
     683                 :          5 :         default:
     684                 :          5 :             return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     685                 :            :     }
     686                 :            : 
     687         [ +  + ]:         40 :     return success ? 0 : -THINGSET_ERR_UNSUPPORTED_FORMAT;
     688                 :            : }
     689                 :            : 
     690                 :         31 : static int bin_deserialize_value(struct thingset_context *ts,
     691                 :            :                                  const struct thingset_data_object *object, bool check_only)
     692                 :            : {
     693                 :            :     int err =
     694                 :         31 :         bin_deserialize_simple_value(ts, object->data, object->type, object->detail, check_only);
     695                 :            : 
     696         [ +  + ]:         31 :     if (err == -THINGSET_ERR_UNSUPPORTED_FORMAT) {
     697                 :            :         bool success;
     698                 :            : 
     699      [ +  -  + ]:          6 :         switch (object->type) {
     700                 :          5 :             case THINGSET_TYPE_ARRAY:
     701                 :          5 :                 struct thingset_array *array = object->data.array;
     702                 :            : 
     703                 :          5 :                 success = zcbor_list_start_decode(ts->decoder);
     704         [ -  + ]:          5 :                 if (!success) {
     705                 :          0 :                     err = -THINGSET_ERR_UNSUPPORTED_FORMAT;
     706                 :          0 :                     break;
     707                 :            :                 }
     708                 :            : 
     709                 :          5 :                 size_t type_size = thingset_type_size(array->element_type);
     710                 :          5 :                 int index = 0;
     711                 :            :                 do {
     712                 :            :                     /* using uint8_t pointer for byte-wise pointer arithmetics */
     713                 :         19 :                     union thingset_data_pointer data = { .u8 = array->elements.u8 + index * type_size };
     714                 :            : 
     715                 :         19 :                     err = bin_deserialize_simple_value(ts, data, array->element_type, array->decimals,
     716                 :            :                                                     check_only);
     717         [ +  + ]:         19 :                     if (err != 0) {
     718                 :          4 :                         break;
     719                 :            :                     }
     720                 :         15 :                     index++;
     721         [ +  + ]:         15 :                 } while (index < array->max_elements);
     722                 :            : 
     723         [ +  + ]:          5 :                 if (!check_only) {
     724                 :          3 :                     array->num_elements = index;
     725                 :            :                 }
     726                 :            : 
     727                 :          5 :                 success = zcbor_list_end_decode(ts->decoder);
     728         [ +  - ]:          5 :                 if (success) {
     729                 :          5 :                     err = 0;
     730                 :            :                 }
     731                 :          5 :                 break;
     732                 :            : 
     733                 :          0 :             case THINGSET_TYPE_RECORDS:
     734                 :          0 :                 struct thingset_records *records = object->data.records;
     735                 :            :                 uint32_t id;
     736                 :            : 
     737                 :          0 :                 success = zcbor_list_start_decode(ts->decoder);
     738         [ #  # ]:          0 :                 for (unsigned int i = 0; i < records->num_records; i++) {
     739                 :          0 :                     success = zcbor_map_start_decode(ts->decoder);
     740         [ #  # ]:          0 :                     if (!success) {
     741                 :          0 :                         err = -THINGSET_ERR_UNSUPPORTED_FORMAT;
     742                 :          0 :                         break;
     743                 :            :                     }
     744                 :            : 
     745   [ #  #  #  # ]:          0 :                     while (zcbor_uint32_decode(ts->decoder, &id) && id < UINT16_MAX) {
     746                 :          0 :                         struct thingset_data_object *element = thingset_get_object_by_id(ts, id);
     747         [ #  # ]:          0 :                         if (element == NULL) {
     748                 :          0 :                             zcbor_any_skip(ts->decoder, NULL);
     749                 :          0 :                             continue;
     750                 :            :                         }
     751                 :          0 :                         union thingset_data_pointer data = { .u8 = ((uint8_t *)records->records)
     752                 :          0 :                                                                 + (i * records->record_size)
     753                 :          0 :                                                                 + element->data.offset };
     754                 :          0 :                         err = bin_deserialize_simple_value(ts, data, element->type, element->detail,
     755                 :            :                                                         check_only);
     756                 :            :                     }
     757                 :            : 
     758                 :          0 :                     success = zcbor_map_end_decode(ts->decoder);
     759                 :            :                 }
     760                 :            : 
     761                 :          0 :                 success = zcbor_list_end_decode(ts->decoder);
     762         [ #  # ]:          0 :                 if (success) {
     763                 :          0 :                     err = 0;
     764                 :            :                 }
     765                 :          0 :                 break;
     766                 :            : 
     767                 :          1 :             default:
     768                 :          1 :                 err = -THINGSET_ERR_UNSUPPORTED_FORMAT;
     769                 :          1 :                 break;
     770                 :            :         }
     771                 :            :     }
     772                 :            : 
     773                 :         31 :     return err;
     774                 :            : }
     775                 :            : 
     776                 :          0 : static int bin_deserialize_skip(struct thingset_context *ts)
     777                 :            : {
     778         [ #  # ]:          0 :     return zcbor_any_skip(ts->decoder, NULL) ? 0 : -THINGSET_ERR_BAD_REQUEST;
     779                 :            : }
     780                 :            : 
     781                 :         11 : static int bin_deserialize_finish(struct thingset_context *ts)
     782                 :            : {
     783         [ +  + ]:         11 :     return ts->decoder->payload_end == ts->decoder->payload ? 0 : -THINGSET_ERR_BAD_REQUEST;
     784                 :            : }
     785                 :            : 
     786                 :            : static struct thingset_api bin_api = {
     787                 :            :     .serialize_response = bin_serialize_response,
     788                 :            :     .serialize_key = bin_serialize_key,
     789                 :            :     .serialize_value = bin_serialize_value,
     790                 :            :     .serialize_key_value = bin_serialize_key_value,
     791                 :            :     .serialize_path = bin_serialize_path,
     792                 :            : #ifdef CONFIG_THINGSET_METADATA_ENDPOINT
     793                 :            :     .serialize_metadata = bin_serialize_metadata,
     794                 :            : #endif
     795                 :            :     .serialize_map_start = bin_serialize_map_start,
     796                 :            :     .serialize_map_end = bin_serialize_map_end,
     797                 :            :     .serialize_list_start = bin_serialize_list_start,
     798                 :            :     .serialize_list_end = bin_serialize_list_end,
     799                 :            :     .serialize_subsets = bin_serialize_subsets,
     800                 :            :     .serialize_report_header = bin_serialize_report_header,
     801                 :            :     .serialize_finish = bin_serialize_finish,
     802                 :            :     .deserialize_payload_reset = bin_deserialize_payload_reset,
     803                 :            :     .deserialize_string = bin_deserialize_string,
     804                 :            :     .deserialize_null = bin_deserialize_null,
     805                 :            :     .deserialize_child = bin_deserialize_child,
     806                 :            :     .deserialize_list_start = bin_deserialize_list_start,
     807                 :            :     .deserialize_map_start = bin_deserialize_map_start,
     808                 :            :     .deserialize_value = bin_deserialize_value,
     809                 :            :     .deserialize_skip = bin_deserialize_skip,
     810                 :            :     .deserialize_finish = bin_deserialize_finish,
     811                 :            : };
     812                 :            : 
     813                 :         78 : inline void thingset_bin_setup(struct thingset_context *ts, size_t rsp_buf_offset)
     814                 :            : {
     815                 :         78 :     ts->api = &bin_api;
     816                 :            : 
     817                 :         78 :     bin_decoder_init(ts, ts->msg + 1, ts->msg_len - 1);
     818                 :            : 
     819                 :         78 :     zcbor_new_encode_state(ts->encoder, ZCBOR_ARRAY_SIZE(ts->encoder), ts->rsp + rsp_buf_offset,
     820                 :         78 :                            ts->rsp_size - rsp_buf_offset, 1);
     821                 :         78 : }
     822                 :            : 
     823                 :          0 : int thingset_bin_import_data_progressively(struct thingset_context *ts, uint8_t auth_flags,
     824                 :            :                                            size_t size, uint32_t *last_id, size_t *consumed)
     825                 :            : {
     826         [ #  # ]:          0 :     if (*last_id == 0) {
     827                 :          0 :         int err = ts->api->deserialize_map_start(ts);
     828         [ #  # ]:          0 :         if (err) {
     829                 :          0 :             return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     830                 :            :         }
     831                 :            :     }
     832                 :            : 
     833                 :            :     /* reset decoder state, but use current decoder payload position and element count
     834                 :            :      * (this handles both the first case, where we've decoded the two bytes of the map start,
     835                 :            :      * and subsequent cases, where we set the payload pointer back to the start of the buffer)
     836                 :            :      */
     837                 :          0 :     zcbor_new_decode_state(ts->decoder, ZCBOR_ARRAY_SIZE(ts->decoder), ts->decoder->payload_mut,
     838                 :          0 :                            size - (ts->decoder->payload - ts->msg), ts->decoder->elem_count, NULL,
     839                 :            :                            0);
     840                 :          0 :     ts->decoder->constant_state->enforce_canonical = false;
     841                 :            : 
     842                 :            :     uint32_t id;
     843                 :          0 :     size_t successfully_parsed_bytes = 0;
     844                 :          0 :     zcbor_state_t state = *ts->decoder;
     845         [ #  # ]:          0 :     while (zcbor_uint32_decode(ts->decoder, &id)) {
     846         [ #  # ]:          0 :         if (id <= UINT16_MAX) {
     847                 :          0 :             const struct thingset_data_object *object = thingset_get_object_by_id(ts, id);
     848   [ #  #  #  # ]:          0 :             if (object != NULL && (object->access & THINGSET_WRITE_MASK & auth_flags) != 0) {
     849         [ #  # ]:          0 :                 if (ts->api->deserialize_value(ts, object, false) == 0) {
     850                 :          0 :                     successfully_parsed_bytes = ts->decoder->payload - ts->msg;
     851                 :            :                 }
     852                 :            :                 else {
     853         [ #  # ]:          0 :                     if (id == *last_id) {
     854                 :            :                         /* we got stuck here last time, so no point going back and asking
     855                 :            :                             for more data; just skip it and move on */
     856         [ #  # ]:          0 :                         if (zcbor_any_skip(ts->decoder, NULL)) {
     857                 :          0 :                             state = *ts->decoder;
     858                 :          0 :                             successfully_parsed_bytes = ts->decoder->payload - ts->msg;
     859                 :            :                         }
     860                 :            :                         else {
     861                 :            :                             /* if we can't even skip the element, the data must be corrupted */
     862                 :          0 :                             *consumed = successfully_parsed_bytes;
     863                 :          0 :                             return -THINGSET_ERR_REQUEST_INCOMPLETE;
     864                 :            :                         }
     865                 :            :                     }
     866                 :            :                     else {
     867                 :            :                         /* reset decoder position to the beginning of the buffer */
     868                 :          0 :                         ts->decoder->payload = ts->msg;
     869                 :          0 :                         ts->decoder->elem_count = state.elem_count;
     870                 :          0 :                         *consumed = successfully_parsed_bytes;
     871                 :          0 :                         *last_id = id;
     872                 :          0 :                         return 1; /* ask for more data */
     873                 :            :                     }
     874                 :            :                 }
     875                 :            :             }
     876                 :            :             else {
     877                 :            :                 /* did not find this object in lookup or was not writable */
     878         [ #  # ]:          0 :                 if (zcbor_any_skip(ts->decoder, NULL)) {
     879                 :          0 :                     state = *ts->decoder;
     880                 :          0 :                     successfully_parsed_bytes = ts->decoder->payload - ts->msg;
     881                 :            :                 }
     882                 :            :                 else {
     883                 :            :                     /* reincrement element count because ID will be parsed again */
     884                 :          0 :                     ts->decoder->elem_count = state.elem_count;
     885                 :            :                 }
     886                 :            :             }
     887                 :          0 :             state = *ts->decoder;
     888                 :          0 :             *last_id = id;
     889                 :            :         }
     890                 :            :     }
     891                 :            : 
     892                 :          0 :     *consumed = successfully_parsed_bytes;
     893   [ #  #  #  # ]:          0 :     if (*consumed == 0 && size > 0) {
     894                 :            :         /* if we didn't manage to consume anything at this point, the data must be completely
     895                 :            :          * invalid, because it means we didn't even parse an ID, so just bail out
     896                 :            :          */
     897                 :          0 :         return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     898                 :            :     }
     899                 :          0 :     bool finished = ts->decoder->payload == ts->decoder->payload_end;
     900                 :          0 :     ts->decoder->payload = ts->msg; /* reset decoder position */
     901                 :          0 :     return finished ? 0 : 1;
     902                 :            : }
     903                 :            : 
     904                 :          2 : int thingset_bin_import_data(struct thingset_context *ts, uint8_t auth_flags,
     905                 :            :                              enum thingset_data_format format)
     906                 :            : {
     907                 :            :     int err;
     908                 :            : 
     909                 :          2 :     err = ts->api->deserialize_map_start(ts);
     910         [ -  + ]:          2 :     if (err != 0) {
     911                 :          0 :         return err;
     912                 :            :     }
     913                 :            : 
     914                 :            :     uint32_t id;
     915         [ +  + ]:          9 :     while (zcbor_uint32_decode(ts->decoder, &id)) {
     916         [ +  - ]:          7 :         if (id <= UINT16_MAX) {
     917                 :          7 :             const struct thingset_data_object *object = thingset_get_object_by_id(ts, id);
     918         [ +  + ]:          7 :             if (object != NULL) {
     919         [ +  - ]:          6 :                 if ((object->access & THINGSET_WRITE_MASK & auth_flags) != 0) {
     920                 :          6 :                     err = ts->api->deserialize_value(ts, object, false);
     921         [ +  - ]:          6 :                     if (err == 0) {
     922                 :          6 :                         continue;
     923                 :            :                     }
     924                 :            :                 }
     925                 :            :             }
     926                 :            :         }
     927                 :            :         /* silently ignore this item if it caused an error */
     928                 :          1 :         zcbor_any_skip(ts->decoder, NULL);
     929                 :            :     }
     930                 :            : 
     931                 :          2 :     return ts->api->deserialize_finish(ts);
     932                 :            : }
     933                 :            : 
     934                 :          3 : int thingset_bin_import_report(struct thingset_context *ts, uint8_t auth_flags, uint16_t subset)
     935                 :            : {
     936                 :          3 :     uint32_t id = 0;
     937                 :            : 
     938         [ +  + ]:          3 :     if (ts->msg_payload[0] != THINGSET_BIN_REPORT) {
     939                 :          1 :         return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     940                 :            :     }
     941                 :            : 
     942                 :          2 :     zcbor_uint32_decode(ts->decoder, &id);
     943         [ +  + ]:          2 :     if (id != subset) {
     944                 :          1 :         return -THINGSET_ERR_NOT_FOUND;
     945                 :            :     }
     946                 :            : 
     947                 :          1 :     return thingset_bin_import_data(ts, auth_flags, subset);
     948                 :            : }
     949                 :            : 
     950                 :         64 : int thingset_bin_process(struct thingset_context *ts)
     951                 :            : {
     952                 :            :     int ret;
     953                 :            : 
     954                 :         64 :     thingset_bin_setup(ts, 1);
     955                 :            : 
     956                 :         64 :     ret = bin_parse_endpoint(ts);
     957         [ +  + ]:         64 :     if (ret != 0) {
     958                 :          1 :         ts->api->serialize_finish(ts);
     959                 :          1 :         return ts->rsp_pos;
     960                 :            :     }
     961                 :            : 
     962                 :         63 :     ts->api->serialize_response(ts, THINGSET_STATUS_CONTENT, NULL);
     963                 :            : 
     964                 :            :     /* requests ordered with expected highest probability first */
     965   [ +  +  +  +  :         63 :     switch (ts->msg[0]) {
             +  +  +  - ]
     966                 :         17 :         case THINGSET_BIN_GET:
     967                 :         17 :             ret = thingset_common_get(ts);
     968                 :         17 :             break;
     969                 :         20 :         case THINGSET_BIN_FETCH:
     970                 :         20 :             ret = thingset_common_fetch(ts);
     971                 :         20 :             break;
     972                 :         10 :         case THINGSET_BIN_UPDATE:
     973                 :         10 :             ret = thingset_common_update(ts);
     974                 :         10 :             break;
     975                 :         13 :         case THINGSET_BIN_EXEC:
     976                 :         13 :             ret = thingset_common_exec(ts);
     977                 :         13 :             break;
     978                 :          1 :         case THINGSET_BIN_CREATE:
     979                 :          1 :             ret = thingset_common_create(ts);
     980                 :          1 :             break;
     981                 :          1 :         case THINGSET_BIN_DELETE:
     982                 :          1 :             ret = thingset_common_delete(ts);
     983                 :          1 :             break;
     984                 :          1 :         case THINGSET_BIN_DESIRE:
     985                 :          1 :             ret = thingset_bin_desire(ts);
     986                 :          1 :             break;
     987                 :          0 :         default:
     988                 :          0 :             return -THINGSET_ERR_BAD_REQUEST;
     989                 :            :     }
     990         [ +  + ]:         63 :     if (ts->msg[0] != THINGSET_BIN_DESIRE) {
     991                 :         62 :         ts->api->serialize_finish(ts);
     992                 :         62 :         return ts->rsp_pos;
     993                 :            :     }
     994                 :            :     else {
     995                 :          1 :         ts->rsp_pos = 0;
     996                 :          1 :         return ret;
     997                 :            :     }
     998                 :            : }

Generated by: LCOV version 1.14