LCOV - code coverage report
Current view: top level - src - thingset_txt.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 393 516 76.2 %
Date: 2025-02-03 11:19:13 Functions: 31 33 93.9 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 198 281 70.5 %

           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                 :            : /* this must be included before thingset.h */
       8                 :            : #include "jsmn.h"
       9                 :            : 
      10                 :            : #include <thingset.h>
      11                 :            : 
      12                 :            : #include "thingset_internal.h"
      13                 :            : 
      14                 :            : #include <errno.h>
      15                 :            : #include <inttypes.h>
      16                 :            : #include <math.h>
      17                 :            : #include <stdarg.h>
      18                 :            : #include <stdio.h>
      19                 :            : #include <stdlib.h>
      20                 :            : #include <string.h>
      21                 :            : 
      22                 :            : #if CONFIG_THINGSET_BYTES_TYPE_SUPPORT
      23                 :            : #include <zephyr/sys/base64.h>
      24                 :            : #endif
      25                 :            : 
      26                 :            : #if CONFIG_THINGSET_JSON_STRING_ESCAPING
      27                 :            : #include <zephyr/sys/util.h>
      28                 :            : #endif
      29                 :            : 
      30                 :         24 : static inline int txt_serialize_start(struct thingset_context *ts, char c)
      31                 :            : {
      32         [ +  - ]:         24 :     if (ts->rsp_size > ts->rsp_pos + 2) {
      33                 :         24 :         ts->rsp[ts->rsp_pos++] = c;
      34                 :         24 :         return 0;
      35                 :            :     }
      36                 :            :     else {
      37                 :          0 :         ts->rsp_pos = 0;
      38                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
      39                 :            :     }
      40                 :            : }
      41                 :            : 
      42                 :         21 : static inline int txt_serialize_end(struct thingset_context *ts, char c)
      43                 :            : {
      44         [ +  - ]:         21 :     if (ts->rsp_size > ts->rsp_pos + 3) {
      45         [ +  - ]:         21 :         if (ts->rsp[ts->rsp_pos - 1] == ',') {
      46                 :         21 :             ts->rsp_pos--;
      47                 :            :         }
      48                 :         21 :         ts->rsp[ts->rsp_pos++] = c;
      49                 :         21 :         ts->rsp[ts->rsp_pos++] = ',';
      50                 :         21 :         return 0;
      51                 :            :     }
      52                 :            :     else {
      53                 :          0 :         ts->rsp_pos = 0;
      54                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
      55                 :            :     }
      56                 :            : }
      57                 :            : 
      58                 :         10 : static int txt_serialize_map_start(struct thingset_context *ts)
      59                 :            : {
      60                 :         10 :     return txt_serialize_start(ts, '{');
      61                 :            : }
      62                 :            : 
      63                 :         10 : static int txt_serialize_map_end(struct thingset_context *ts)
      64                 :            : {
      65                 :         10 :     return txt_serialize_end(ts, '}');
      66                 :            : }
      67                 :            : 
      68                 :         14 : static int txt_serialize_list_start(struct thingset_context *ts)
      69                 :            : {
      70                 :         14 :     return txt_serialize_start(ts, '[');
      71                 :            : }
      72                 :            : 
      73                 :         11 : static int txt_serialize_list_end(struct thingset_context *ts)
      74                 :            : {
      75                 :         11 :     return txt_serialize_end(ts, ']');
      76                 :            : }
      77                 :            : 
      78                 :         67 : static int txt_serialize_response(struct thingset_context *ts, uint8_t code, const char *msg, ...)
      79                 :            : {
      80                 :            :     va_list vargs;
      81                 :            : 
      82                 :         67 :     ts->rsp_pos = snprintf((char *)ts->rsp, ts->rsp_size, ":%.2X ", code);
      83                 :            : 
      84   [ +  +  +  - ]:         67 :     if (msg != NULL && ts->rsp_size > 7) {
      85                 :         16 :         ts->rsp[ts->rsp_pos++] = '"';
      86                 :         16 :         va_start(vargs, msg);
      87                 :         16 :         ts->rsp_pos +=
      88                 :         16 :             vsnprintf((char *)ts->rsp + ts->rsp_pos, ts->rsp_size - ts->rsp_pos, msg, vargs);
      89                 :         16 :         va_end(vargs);
      90         [ +  - ]:         16 :         if (ts->rsp_pos + 1 < ts->rsp_size) {
      91                 :         16 :             ts->rsp[ts->rsp_pos++] = '"';
      92                 :            :         }
      93                 :            :         else {
      94                 :            :             /* message did not fit: keep minimum message with error code only */
      95                 :          0 :             ts->rsp_pos = 3;
      96                 :            :         }
      97                 :         16 :         ts->rsp[ts->rsp_pos++] = ' ';
      98                 :            :     }
      99                 :            : 
     100                 :         67 :     return 0;
     101                 :            : }
     102                 :            : 
     103                 :            : /**
     104                 :            :  * @returns Number of serialized bytes or negative ThingSet reponse code in case of error
     105                 :            :  */
     106                 :         96 : static int json_serialize_simple_value(char *buf, size_t size, union thingset_data_pointer data,
     107                 :            :                                        int type, int detail)
     108                 :            : {
     109                 :            :     int pos;
     110                 :            : 
     111   [ +  +  +  +  :         96 :     switch (type) {
          +  +  +  +  +  
             +  +  +  -  
                      + ]
     112                 :            : #if CONFIG_THINGSET_64BIT_TYPES_SUPPORT
     113                 :          2 :         case THINGSET_TYPE_U64:
     114                 :          2 :             pos = snprintf(buf, size, "%" PRIu64 ",", *data.u64);
     115                 :          2 :             break;
     116                 :          2 :         case THINGSET_TYPE_I64:
     117                 :          2 :             pos = snprintf(buf, size, "%" PRIi64 ",", *data.i64);
     118                 :          2 :             break;
     119                 :            : #endif
     120                 :         10 :         case THINGSET_TYPE_U32:
     121                 :         10 :             pos = snprintf(buf, size, "%" PRIu32 ",", *data.u32);
     122                 :         10 :             break;
     123                 :         11 :         case THINGSET_TYPE_I32:
     124                 :         11 :             pos = snprintf(buf, size, "%" PRIi32 ",", *data.i32);
     125                 :         11 :             break;
     126                 :          2 :         case THINGSET_TYPE_U16:
     127                 :          2 :             pos = snprintf(buf, size, "%" PRIu16 ",", *data.u16);
     128                 :          2 :             break;
     129                 :          2 :         case THINGSET_TYPE_I16:
     130                 :          2 :             pos = snprintf(buf, size, "%" PRIi16 ",", *data.i16);
     131                 :          2 :             break;
     132                 :          2 :         case THINGSET_TYPE_U8:
     133                 :          2 :             pos = snprintf(buf, size, "%" PRIu8 ",", *data.u8);
     134                 :          2 :             break;
     135                 :          2 :         case THINGSET_TYPE_I8:
     136                 :          2 :             pos = snprintf(buf, size, "%" PRIi8 ",", *data.i8);
     137                 :          2 :             break;
     138                 :         25 :         case THINGSET_TYPE_F32:
     139   [ +  +  +  + ]:         25 :             if (isnan(*data.f32) || isinf(*data.f32)) {
     140                 :            :                 /* JSON spec does not support NaN and Inf, so we need to use null instead */
     141                 :          2 :                 pos = snprintf(buf, size, "null,");
     142                 :          2 :                 break;
     143                 :            :             }
     144                 :            :             else {
     145                 :            :                 /* explicit double-conversion required to please compiler */
     146                 :         23 :                 pos = snprintf(buf, size, "%.*f,", detail, (double)*data.f32);
     147                 :         23 :                 break;
     148                 :            :             }
     149                 :            : #if CONFIG_THINGSET_DECFRAC_TYPE_SUPPORT
     150                 :          2 :         case THINGSET_TYPE_DECFRAC:
     151                 :          2 :             pos = snprintf(buf, size, "%" PRIi32 "e%" PRIi16 ",", *data.decfrac, -detail);
     152                 :          2 :             break;
     153                 :            : #endif
     154                 :          5 :         case THINGSET_TYPE_BOOL:
     155         [ +  - ]:          5 :             pos = snprintf(buf, size, "%s,", *data.b == true ? "true" : "false");
     156                 :          5 :             break;
     157                 :          5 :         case THINGSET_TYPE_STRING:
     158                 :            : #if CONFIG_THINGSET_JSON_STRING_ESCAPING
     159                 :          5 :             buf[0] = '"';
     160                 :          5 :             pos = 1;
     161         [ +  - ]:         36 :             for (int data_pos = 0; data_pos < detail; data_pos++) {
     162                 :         36 :                 int remaining_chars = detail - data_pos;
     163         [ -  + ]:         36 :                 if (pos + remaining_chars + 2 >= size) {
     164                 :            :                     /* indicate that the buffer is too small (similar to snprintf) and stop */
     165                 :          0 :                     pos += remaining_chars + 2;
     166                 :          0 :                     break;
     167                 :            :                 }
     168                 :            : 
     169         [ +  + ]:         36 :                 if (data.str[data_pos] == '\0') {
     170                 :          5 :                     break;
     171                 :            :                 }
     172                 :            : 
     173   [ +  +  -  -  :         31 :                 switch (data.str[data_pos]) {
             +  -  -  + ]
     174                 :          1 :                     case '\\':
     175                 :          1 :                         buf[pos++] = '\\';
     176                 :          1 :                         buf[pos++] = '\\';
     177                 :          1 :                         break;
     178                 :          1 :                     case '\"':
     179                 :          1 :                         buf[pos++] = '\\';
     180                 :          1 :                         buf[pos++] = '"';
     181                 :          1 :                         break;
     182                 :          0 :                     case '\b':
     183                 :          0 :                         buf[pos++] = '\\';
     184                 :          0 :                         buf[pos++] = 'b';
     185                 :          0 :                         break;
     186                 :          0 :                     case '\f':
     187                 :          0 :                         buf[pos++] = '\\';
     188                 :          0 :                         buf[pos++] = 'f';
     189                 :          0 :                         break;
     190                 :          1 :                     case '\n':
     191                 :          1 :                         buf[pos++] = '\\';
     192                 :          1 :                         buf[pos++] = 'n';
     193                 :          1 :                         break;
     194                 :          0 :                     case '\r':
     195                 :          0 :                         buf[pos++] = '\\';
     196                 :          0 :                         buf[pos++] = 'r';
     197                 :          0 :                         break;
     198                 :          0 :                     case '\t':
     199                 :          0 :                         buf[pos++] = '\\';
     200                 :          0 :                         buf[pos++] = 't';
     201                 :          0 :                         break;
     202                 :         28 :                     default:
     203                 :         28 :                         buf[pos++] = data.str[data_pos];
     204                 :            :                 }
     205                 :            :             }
     206                 :          5 :             buf[pos++] = '"';
     207                 :          5 :             buf[pos++] = ',';
     208                 :            : #else
     209                 :            :             pos = snprintf(buf, size, "\"%s\",", data.str);
     210                 :            : #endif
     211                 :          5 :             break;
     212                 :            : #if CONFIG_THINGSET_BYTES_TYPE_SUPPORT
     213                 :          0 :         case THINGSET_TYPE_BYTES: {
     214                 :            :             size_t strlen;
     215                 :          0 :             int err = base64_encode((uint8_t *)buf + 1, size - 4, &strlen, data.bytes->bytes,
     216                 :          0 :                                     data.bytes->num_bytes);
     217         [ #  # ]:          0 :             if (err == 0) {
     218                 :          0 :                 buf[0] = '\"';
     219                 :          0 :                 buf[strlen + 1] = '\"';
     220                 :          0 :                 buf[strlen + 2] = ',';
     221                 :          0 :                 buf[strlen + 3] = '\0';
     222                 :          0 :                 pos = strlen + 3;
     223                 :            :             }
     224                 :            :             else {
     225                 :          0 :                 pos = snprintf(buf, size, "null,");
     226                 :            :             }
     227                 :          0 :             break;
     228                 :            :         }
     229                 :            : #endif
     230                 :         26 :         default:
     231                 :         26 :             return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     232                 :            :     }
     233                 :            : 
     234   [ +  -  +  - ]:         70 :     if (pos >= 0 && pos < size) {
     235                 :         70 :         return pos;
     236                 :            :     }
     237                 :            :     else {
     238                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     239                 :            :     }
     240                 :            : }
     241                 :            : 
     242                 :         84 : static int txt_serialize_value(struct thingset_context *ts,
     243                 :            :                                const struct thingset_data_object *object)
     244                 :            : {
     245                 :         84 :     char *buf = ts->rsp + ts->rsp_pos;
     246                 :         84 :     size_t size = ts->rsp_size - ts->rsp_pos;
     247                 :            :     int ret;
     248                 :            : 
     249                 :         84 :     int pos = json_serialize_simple_value(buf, size, object->data, object->type, object->detail);
     250                 :            : 
     251         [ +  + ]:         84 :     if (pos == -THINGSET_ERR_UNSUPPORTED_FORMAT) {
     252                 :            :         /* not a simple value */
     253         [ +  + ]:         26 :         if (object->type == THINGSET_TYPE_GROUP) {
     254                 :          7 :             pos = snprintf(buf, size, "null,");
     255                 :            :         }
     256         [ +  + ]:         19 :         else if (object->type == THINGSET_TYPE_RECORDS) {
     257                 :            :             if (IS_ENABLED(CONFIG_THINGSET_REPORT_RECORD_SERIALIZATION)
     258                 :            :                 && ts->rsp[0] == THINGSET_TXT_REPORT)
     259                 :            :             {
     260                 :            :                 pos = snprintf(buf, size, "[");
     261                 :            :                 ts->rsp_pos++;
     262                 :            :                 for (unsigned int i = 0; i < object->data.records->num_records; i++) {
     263                 :            :                     ret = thingset_common_serialize_record(ts, object, i);
     264                 :            :                     pos = ts->rsp_pos;
     265                 :            :                     buf[pos++] = ',';
     266                 :            :                 }
     267                 :            :                 /* thingset_common_serialize_record has already incremeneted rsp_pos, so we reset
     268                 :            :                    the length of pos here, so that when we increment at the end, we are only
     269                 :            :                    incrementing the small delta below */
     270                 :            :                 buf = ts->rsp + ts->rsp_pos;
     271                 :            :                 pos = 0;
     272                 :            :                 if (object->data.records->num_records > 0) {
     273                 :            :                     pos--; /* remove trailing comma */
     274                 :            :                 }
     275                 :            :                 pos += snprintf(buf + pos, size - pos, "],");
     276                 :            :             }
     277                 :            :             else {
     278                 :          7 :                 pos = snprintf(buf, size, "%d,", object->data.records->num_records);
     279                 :            :             }
     280                 :            :         }
     281   [ +  +  +  + ]:         12 :         else if (object->type == THINGSET_TYPE_FN_VOID || object->type == THINGSET_TYPE_FN_I32) {
     282                 :          4 :             pos = snprintf(buf, size, "[");
     283         [ +  + ]:        288 :             for (unsigned int i = 0; i < ts->num_objects; i++) {
     284         [ +  + ]:        284 :                 if (ts->data_objects[i].parent_id == object->id) {
     285                 :          3 :                     pos += snprintf(buf + pos, size - pos, "\"%s\",", ts->data_objects[i].name);
     286                 :            :                 }
     287                 :            :             }
     288         [ +  + ]:          4 :             if (pos > 1) {
     289                 :          2 :                 pos--; /* remove trailing comma */
     290                 :            :             }
     291                 :          4 :             pos += snprintf(buf + pos, size - pos, "],");
     292                 :            :         }
     293         [ +  + ]:          8 :         else if (object->type == THINGSET_TYPE_SUBSET) {
     294                 :          4 :             pos = snprintf(buf, size, "[");
     295         [ +  + ]:        288 :             for (unsigned int i = 0; i < ts->num_objects; i++) {
     296         [ +  + ]:        284 :                 if (ts->data_objects[i].subsets & object->data.subset) {
     297                 :         19 :                     buf[pos++] = '"';
     298                 :         19 :                     ret = thingset_get_path(ts, buf + pos, size - pos, &ts->data_objects[i]);
     299         [ -  + ]:         19 :                     if (ret <= 0) {
     300                 :          0 :                         ts->rsp_pos = 0;
     301                 :          0 :                         return ret;
     302                 :            :                     }
     303                 :         19 :                     pos += ret;
     304                 :         19 :                     buf[pos++] = '"';
     305                 :         19 :                     buf[pos++] = ',';
     306                 :            :                 }
     307                 :            :             }
     308         [ +  - ]:          4 :             if (pos > 1) {
     309                 :          4 :                 pos--; /* remove trailing comma */
     310                 :            :             }
     311                 :          4 :             pos += snprintf(buf + pos, size - pos, "],");
     312                 :            :         }
     313   [ +  -  +  - ]:          4 :         else if (object->type == THINGSET_TYPE_ARRAY && object->data.array != NULL) {
     314                 :          4 :             struct thingset_array *array = object->data.array;
     315                 :          4 :             pos = snprintf(buf, size, "[");
     316                 :          4 :             size_t type_size = thingset_type_size(array->element_type);
     317         [ +  + ]:         16 :             for (int i = 0; i < array->num_elements; i++) {
     318                 :            :                 /* using uint8_t pointer for byte-wise pointer arithmetics */
     319                 :         12 :                 union thingset_data_pointer data = { .u8 = array->elements.u8 + i * type_size };
     320                 :         12 :                 pos += json_serialize_simple_value(buf + pos, size - pos, data, array->element_type,
     321                 :         12 :                                                    array->decimals);
     322                 :            :             }
     323         [ +  - ]:          4 :             if (array->num_elements > 0) {
     324                 :          4 :                 pos--; /* remove trailing comma */
     325                 :            :             }
     326                 :          4 :             pos += snprintf(buf + pos, size - pos, "],");
     327                 :            :         }
     328                 :            :         else {
     329                 :          0 :             ts->rsp_pos = 0;
     330                 :          0 :             return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     331                 :            :         }
     332                 :            :     }
     333                 :            : 
     334   [ +  -  +  - ]:         84 :     if (pos >= 0 && pos < size) {
     335                 :         84 :         ts->rsp_pos += pos;
     336                 :         84 :         return 0;
     337                 :            :     }
     338                 :            :     else {
     339                 :          0 :         ts->rsp_pos = 0;
     340                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     341                 :            :     }
     342                 :            : }
     343                 :            : 
     344                 :          0 : static int txt_serialize_path(struct thingset_context *ts,
     345                 :            :                               const struct thingset_data_object *object)
     346                 :            : {
     347                 :            :     /* not used for text mode */
     348                 :            : 
     349                 :          0 :     return -THINGSET_ERR_INTERNAL_SERVER_ERR;
     350                 :            : }
     351                 :            : 
     352                 :         85 : static int txt_serialize_string(struct thingset_context *ts, const char *buf, bool is_key)
     353                 :            : {
     354         [ +  + ]:         85 :     int len = snprintf(ts->rsp + ts->rsp_pos, ts->rsp_size - ts->rsp_pos, "\"%s\"%s", buf,
     355                 :            :                        is_key ? ":" : ",");
     356   [ +  -  +  - ]:         85 :     if (len >= 0 && len < ts->rsp_size - ts->rsp_pos) {
     357                 :         85 :         ts->rsp_pos += len;
     358                 :         85 :         return 0;
     359                 :            :     }
     360                 :            :     else {
     361                 :          0 :         ts->rsp_pos = 0;
     362                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     363                 :            :     }
     364                 :            : }
     365                 :            : 
     366                 :         16 : static int txt_serialize_name(struct thingset_context *ts,
     367                 :            :                               const struct thingset_data_object *object)
     368                 :            : {
     369                 :         16 :     return txt_serialize_string(ts, object->name, false);
     370                 :            : }
     371                 :            : 
     372                 :            : #ifdef CONFIG_THINGSET_METADATA_ENDPOINT
     373                 :          1 : static int txt_serialize_metadata(struct thingset_context *ts,
     374                 :            :                                   const struct thingset_data_object *object)
     375                 :            : {
     376                 :          1 :     int err = txt_serialize_map_start(ts);
     377         [ -  + ]:          1 :     if (err) {
     378                 :          0 :         return err;
     379                 :            :     }
     380                 :            : 
     381         [ -  + ]:          1 :     if ((err = txt_serialize_string(ts, "name", true))) {
     382                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     383                 :            :     }
     384                 :            : 
     385         [ -  + ]:          1 :     if ((err = txt_serialize_name(ts, object))) {
     386                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     387                 :            :     }
     388                 :            : 
     389         [ -  + ]:          1 :     if ((err = txt_serialize_string(ts, "type", true))) {
     390                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     391                 :            :     }
     392                 :            : 
     393                 :            :     char buf[128];
     394                 :          1 :     int len = thingset_get_type_name(ts, object, (char *)&buf, sizeof(buf));
     395         [ -  + ]:          1 :     if (len < 0) {
     396                 :          0 :         return THINGSET_ERR_RESPONSE_TOO_LARGE;
     397                 :            :     }
     398                 :            : 
     399         [ -  + ]:          1 :     if ((err = txt_serialize_string(ts, buf, false))) {
     400                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     401                 :            :     }
     402                 :            : 
     403         [ -  + ]:          1 :     if ((err = txt_serialize_map_end(ts))) {
     404                 :          0 :         return err;
     405                 :            :     }
     406                 :            : 
     407                 :          1 :     return 0;
     408                 :            : }
     409                 :            : #endif /* CONFIG_THINGSET_METADATA_ENDPOINT */
     410                 :            : 
     411                 :         66 : static int txt_serialize_name_value(struct thingset_context *ts,
     412                 :            :                                     const struct thingset_data_object *object)
     413                 :            : {
     414                 :            :     int err;
     415                 :            : 
     416                 :         66 :     err = txt_serialize_string(ts, object->name, true);
     417         [ -  + ]:         66 :     if (err != 0) {
     418                 :          0 :         return err;
     419                 :            :     }
     420                 :            : 
     421                 :         66 :     return ts->api->serialize_value(ts, object);
     422                 :            : }
     423                 :            : 
     424                 :         69 : static void txt_serialize_finish(struct thingset_context *ts)
     425                 :            : {
     426                 :            :     /* remove the trailing comma or space (in case of no payload) and terminate string */
     427                 :         69 :     ts->rsp_pos--;
     428                 :         69 :     ts->rsp[ts->rsp_pos] = '\0';
     429                 :         69 : }
     430                 :            : 
     431                 :            : /**
     432                 :            :  * @returns 0 or negative ThingSet reponse code in case of error
     433                 :            :  */
     434                 :         65 : static int txt_parse_endpoint(struct thingset_context *ts)
     435                 :            : {
     436                 :         65 :     char *path_begin = (char *)ts->msg + 1;
     437                 :         65 :     char *path_end = memchr(path_begin, ' ', ts->msg_len - 1);
     438                 :            :     int path_len;
     439                 :            : 
     440         [ +  + ]:         65 :     if (path_end != NULL) {
     441                 :         45 :         path_len = path_end - path_begin;
     442                 :            :     }
     443                 :            :     else {
     444                 :         20 :         path_len = ts->msg_len - 1;
     445                 :            :     }
     446                 :            : 
     447                 :         65 :     int err = thingset_endpoint_by_path(ts, &ts->endpoint, path_begin, path_len);
     448         [ +  + ]:         65 :     if (err != 0) {
     449                 :          3 :         return err;
     450                 :            :     }
     451                 :            : 
     452                 :         62 :     ts->msg_pos += path_len + 1;
     453                 :            : 
     454                 :         62 :     return 0;
     455                 :            : }
     456                 :            : 
     457                 :            : /**
     458                 :            :  * @returns 0 or negative ThingSet reponse code in case of error
     459                 :            :  */
     460                 :         73 : static int txt_parse_payload(struct thingset_context *ts)
     461                 :            : {
     462                 :            :     struct jsmn_parser parser;
     463                 :            :     int ret;
     464                 :            : 
     465                 :         73 :     ts->msg_payload = ts->msg + ts->msg_pos;
     466                 :         73 :     ts->tok_pos = 0;
     467                 :            : 
     468                 :         73 :     jsmn_init(&parser);
     469                 :            : 
     470                 :         73 :     ret = jsmn_parse(&parser, ts->msg_payload, ts->msg_len - ts->msg_pos, ts->tokens,
     471                 :            :                      sizeof(ts->tokens));
     472         [ -  + ]:         73 :     if (ret == JSMN_ERROR_NOMEM) {
     473                 :          0 :         ts->rsp_pos = 0;
     474                 :          0 :         return -THINGSET_ERR_REQUEST_TOO_LARGE;
     475                 :            :     }
     476         [ +  + ]:         73 :     else if (ret < 0) {
     477                 :            :         /* other parsing error */
     478                 :          2 :         ts->rsp_pos = 0;
     479                 :          2 :         return -THINGSET_ERR_BAD_REQUEST;
     480                 :            :     }
     481                 :            : 
     482                 :         71 :     ts->tok_count = ret;
     483                 :         71 :     return 0;
     484                 :            : }
     485                 :            : 
     486                 :         27 : int thingset_txt_get_fetch(struct thingset_context *ts)
     487                 :            : {
     488         [ +  + ]:         27 :     if (ts->tok_count == 0) {
     489                 :         13 :         return thingset_common_get(ts);
     490                 :            :     }
     491                 :            :     else {
     492                 :         14 :         return thingset_common_fetch(ts);
     493                 :            :     }
     494                 :            : }
     495                 :            : 
     496                 :         45 : static int txt_deserialize_simple_value(struct thingset_context *ts,
     497                 :            :                                         union thingset_data_pointer data, int type, int detail,
     498                 :            :                                         bool check_only)
     499                 :            : {
     500         [ +  + ]:         45 :     if (ts->tok_pos >= ts->tok_count) {
     501                 :          5 :         return -THINGSET_ERR_DESERIALIZATION_FINISHED;
     502                 :            :     }
     503                 :            : 
     504                 :         40 :     const char *buf = ts->msg_payload + ts->tokens[ts->tok_pos].start;
     505                 :         40 :     size_t len = ts->tokens[ts->tok_pos].end - ts->tokens[ts->tok_pos].start;
     506                 :            : 
     507         [ +  + ]:         40 :     if (ts->tokens[ts->tok_pos].type != JSMN_PRIMITIVE
     508         [ +  + ]:         10 :         && ts->tokens[ts->tok_pos].type != JSMN_STRING)
     509                 :            :     {
     510                 :          4 :         return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     511                 :            :     }
     512                 :            : 
     513                 :         36 :     errno = 0;
     514   [ +  -  -  -  :         36 :     switch (type) {
          +  +  -  -  -  
             -  +  +  +  
                      - ]
     515                 :         16 :         case THINGSET_TYPE_F32:
     516                 :         16 :             *data.f32 = strtod(buf, NULL);
     517                 :         16 :             break;
     518                 :            : #if CONFIG_THINGSET_DECFRAC_TYPE_SUPPORT
     519                 :          0 :         case THINGSET_TYPE_DECFRAC: {
     520                 :          0 :             float tmp = strtod(buf, NULL);
     521                 :            :             /* negative decimals and positive exponent */
     522         [ #  # ]:          0 :             for (int16_t i = 0; i < -detail; i++) {
     523                 :          0 :                 tmp /= 10.0F;
     524                 :            :             }
     525                 :            :             /* positive decimals and negative exponent */
     526         [ #  # ]:          0 :             for (int16_t i = 0; i > -detail; i--) {
     527                 :          0 :                 tmp *= 10.0F;
     528                 :            :             }
     529                 :          0 :             *data.decfrac = (int32_t)tmp;
     530                 :          0 :             break;
     531                 :            :         }
     532                 :            : #endif
     533                 :            : #if CONFIG_THINGSET_64BIT_TYPES_SUPPORT
     534                 :          0 :         case THINGSET_TYPE_U64:
     535                 :          0 :             *data.u64 = strtoull(buf, NULL, 0);
     536                 :          0 :             break;
     537                 :          0 :         case THINGSET_TYPE_I64:
     538                 :          0 :             *data.i64 = strtoll(buf, NULL, 0);
     539                 :          0 :             break;
     540                 :            : #endif
     541                 :          2 :         case THINGSET_TYPE_U32:
     542                 :          2 :             *data.u32 = strtoul(buf, NULL, 0);
     543                 :          2 :             break;
     544                 :          9 :         case THINGSET_TYPE_I32:
     545                 :          9 :             *data.i32 = strtol(buf, NULL, 0);
     546                 :          9 :             break;
     547                 :          0 :         case THINGSET_TYPE_U16:
     548                 :          0 :             *data.u16 = strtoul(buf, NULL, 0);
     549                 :          0 :             break;
     550                 :          0 :         case THINGSET_TYPE_I16:
     551                 :          0 :             *data.i16 = strtol(buf, NULL, 0);
     552                 :          0 :             break;
     553                 :          0 :         case THINGSET_TYPE_U8:
     554                 :          0 :             *data.u8 = strtoul(buf, NULL, 0);
     555                 :          0 :             break;
     556                 :          0 :         case THINGSET_TYPE_I8:
     557                 :          0 :             *data.i8 = strtol(buf, NULL, 0);
     558                 :          0 :             break;
     559                 :          4 :         case THINGSET_TYPE_BOOL:
     560   [ +  +  -  + ]:          4 :             if (buf[0] == 't' || buf[0] == '1') {
     561                 :          2 :                 *data.b = true;
     562                 :            :             }
     563   [ +  +  -  + ]:          2 :             else if (buf[0] == 'f' || buf[0] == '0') {
     564                 :          1 :                 *data.b = false;
     565                 :            :             }
     566                 :            :             else {
     567                 :          1 :                 return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     568                 :            :             }
     569                 :          3 :             break;
     570                 :          3 :         case THINGSET_TYPE_STRING:
     571   [ +  -  -  + ]:          3 :             if (ts->tokens[ts->tok_pos].type != JSMN_STRING || (unsigned int)detail <= len) {
     572                 :          0 :                 return -THINGSET_ERR_REQUEST_TOO_LARGE;
     573                 :            :             }
     574         [ +  + ]:          3 :             if (!check_only) {
     575                 :            : #if CONFIG_THINGSET_JSON_STRING_ESCAPING
     576                 :          2 :                 size_t data_pos = 0;
     577         [ +  + ]:          9 :                 for (int pos = 0; pos < len; pos++) {
     578         [ +  + ]:          7 :                     if (buf[pos] == '\\') {
     579                 :          3 :                         pos++;
     580   [ +  -  -  +  :          3 :                         switch (buf[pos]) {
             -  -  -  - ]
     581                 :          2 :                             case '"':
     582                 :            :                             case '/':
     583                 :            :                             case '\\':
     584                 :          2 :                                 data.str[data_pos++] = buf[pos];
     585                 :          2 :                                 break;
     586                 :          0 :                             case 'b':
     587                 :          0 :                                 data.str[data_pos++] = '\b';
     588                 :          0 :                                 break;
     589                 :          0 :                             case 'f':
     590                 :          0 :                                 data.str[data_pos++] = '\f';
     591                 :          0 :                                 break;
     592                 :          1 :                             case 'n':
     593                 :          1 :                                 data.str[data_pos++] = '\n';
     594                 :          1 :                                 break;
     595                 :          0 :                             case 'r':
     596                 :          0 :                                 data.str[data_pos++] = '\r';
     597                 :          0 :                                 break;
     598                 :          0 :                             case 't':
     599                 :          0 :                                 data.str[data_pos++] = '\t';
     600                 :          0 :                                 break;
     601                 :          0 :                             case 'u':
     602                 :          0 :                                 hex2bin(&buf[pos], 4, &data.str[data_pos++], 2);
     603                 :          0 :                                 break;
     604                 :          0 :                             default:
     605                 :            :                                 /* this would be invalid JSON */
     606                 :          0 :                                 return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     607                 :            :                         }
     608                 :            :                     }
     609                 :            :                     else {
     610                 :          4 :                         data.str[data_pos++] = buf[pos];
     611                 :            :                     }
     612                 :            :                 }
     613                 :            : #else
     614                 :            :                 strncpy(data.str, buf, len);
     615                 :            :                 data.str[len] = '\0';
     616                 :            : #endif
     617                 :            :             }
     618                 :          3 :             break;
     619                 :            : #if CONFIG_THINGSET_BYTES_TYPE_SUPPORT
     620                 :          2 :         case THINGSET_TYPE_BYTES: {
     621   [ +  -  -  + ]:          2 :             if (ts->tokens[ts->tok_pos].type != JSMN_STRING || data.bytes->max_bytes < len / 4 * 3)
     622                 :            :             {
     623                 :          0 :                 return -THINGSET_ERR_REQUEST_TOO_LARGE;
     624                 :            :             }
     625         [ +  + ]:          2 :             if (!check_only) {
     626                 :          1 :                 struct thingset_bytes *bytes_buf = data.bytes;
     627                 :            :                 size_t byteslen;
     628                 :          1 :                 int err = base64_decode(bytes_buf->bytes, bytes_buf->max_bytes, &byteslen,
     629                 :            :                                         (uint8_t *)buf, len);
     630                 :          1 :                 bytes_buf->num_bytes = byteslen;
     631         [ -  + ]:          1 :                 if (err != 0) {
     632                 :          0 :                     return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     633                 :            :                 }
     634                 :            :             }
     635                 :          2 :             break;
     636                 :            :         }
     637                 :            : #endif
     638                 :          0 :         default:
     639                 :          0 :             return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     640                 :            :     }
     641                 :            : 
     642         [ -  + ]:         35 :     if (errno == ERANGE) {
     643                 :          0 :         return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     644                 :            :     }
     645                 :            : 
     646                 :         35 :     ts->tok_pos++;
     647                 :         35 :     return 0;
     648                 :            : }
     649                 :            : 
     650                 :         29 : static int txt_deserialize_value(struct thingset_context *ts,
     651                 :            :                                  const struct thingset_data_object *object, bool check_only)
     652                 :            : {
     653                 :            :     int err =
     654                 :         29 :         txt_deserialize_simple_value(ts, object->data, object->type, object->detail, check_only);
     655                 :            : 
     656   [ +  +  +  + ]:         29 :     if (err == -THINGSET_ERR_UNSUPPORTED_FORMAT && object->type == THINGSET_TYPE_ARRAY) {
     657                 :          4 :         struct thingset_array *array = object->data.array;
     658                 :            : 
     659                 :          4 :         err = ts->api->deserialize_list_start(ts);
     660         [ -  + ]:          4 :         if (err != 0) {
     661                 :          0 :             return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     662                 :            :         }
     663                 :            : 
     664                 :          4 :         size_t type_size = thingset_type_size(array->element_type);
     665                 :          4 :         int index = 0;
     666                 :            :         do {
     667                 :            :             /* using uint8_t pointer for byte-wise pointer arithmetics */
     668                 :         16 :             union thingset_data_pointer data = { .u8 = array->elements.u8 + index * type_size };
     669                 :            : 
     670                 :         16 :             err = txt_deserialize_simple_value(ts, data, array->element_type, array->decimals,
     671                 :            :                                                check_only);
     672         [ +  + ]:         16 :             if (err != 0) {
     673                 :          4 :                 break;
     674                 :            :             }
     675                 :         12 :             index++;
     676         [ +  - ]:         12 :         } while (index < array->max_elements);
     677                 :            : 
     678         [ +  + ]:          4 :         if (!check_only) {
     679                 :          2 :             array->num_elements = index;
     680                 :            :         }
     681                 :            : 
     682         [ +  - ]:          4 :         if (err == -THINGSET_ERR_DESERIALIZATION_FINISHED) {
     683                 :          4 :             err = 0;
     684                 :            :         };
     685                 :            :     }
     686                 :            : 
     687                 :         29 :     return err;
     688                 :            : }
     689                 :            : 
     690                 :          1 : int thingset_txt_desire(struct thingset_context *ts)
     691                 :            : {
     692                 :          1 :     return -THINGSET_ERR_NOT_IMPLEMENTED;
     693                 :            : }
     694                 :            : 
     695                 :            : /* currently only supporting nesting of depth 2 (parent and grandparent != 0) */
     696                 :          2 : static int txt_serialize_subsets(struct thingset_context *ts, uint16_t subsets)
     697                 :            : {
     698                 :            :     struct thingset_data_object *ancestors[2];
     699                 :          2 :     int depth = 0;
     700                 :            : 
     701                 :          2 :     ts->rsp[ts->rsp_pos++] = '{';
     702                 :            : 
     703         [ +  + ]:        144 :     for (unsigned int i = 0; i < ts->num_objects; i++) {
     704         [ +  + ]:        142 :         if (ts->data_objects[i].subsets & subsets) {
     705                 :         10 :             const uint16_t parent_id = ts->data_objects[i].parent_id;
     706                 :            : 
     707                 :         10 :             struct thingset_data_object *parent = NULL;
     708   [ +  +  -  + ]:         10 :             if (depth > 0 && parent_id == ancestors[depth - 1]->id) {
     709                 :            :                 /* same parent as previous item */
     710                 :          0 :                 parent = ancestors[depth - 1];
     711                 :            :             }
     712         [ +  + ]:         10 :             else if (parent_id != 0) {
     713                 :            :                 /* parent needs to be searched in the object database */
     714                 :          6 :                 parent = thingset_get_object_by_id(ts, parent_id);
     715                 :            :             }
     716                 :            : 
     717                 :            :             /* close object if previous object had different parent or grandparent */
     718   [ +  +  +  - ]:         10 :             if (depth > 0 && parent_id != ancestors[depth - 1]->id
     719   [ +  +  +  - ]:          4 :                 && ((parent != NULL && parent->parent_id != ancestors[depth - 1]->id)
     720         [ +  + ]:          4 :                     || parent_id == 0)) /* return to root */
     721                 :            :             {
     722                 :          2 :                 ts->rsp[ts->rsp_pos - 1] = '}'; /* overwrite comma */
     723                 :          2 :                 ts->rsp[ts->rsp_pos++] = ',';
     724                 :          2 :                 depth--;
     725                 :            :             }
     726                 :            : 
     727   [ +  +  +  + ]:         10 :             if (depth == 0 && parent != NULL) {
     728         [ -  + ]:          4 :                 if (parent->parent_id != 0) {
     729                 :            :                     struct thingset_data_object *grandparent =
     730                 :          0 :                         thingset_get_object_by_id(ts, parent->parent_id);
     731         [ #  # ]:          0 :                     if (grandparent != NULL) {
     732                 :          0 :                         ts->rsp_pos += snprintf(ts->rsp + ts->rsp_pos, ts->rsp_size - ts->rsp_pos,
     733                 :            :                                                 "\"%s\":{", grandparent->name);
     734                 :          0 :                         ancestors[depth++] = grandparent;
     735                 :            :                     }
     736                 :            :                 }
     737                 :          4 :                 ts->rsp_pos += snprintf(ts->rsp + ts->rsp_pos, ts->rsp_size - ts->rsp_pos,
     738                 :            :                                         "\"%s\":{", parent->name);
     739                 :          4 :                 ancestors[depth++] = parent;
     740                 :            :             }
     741   [ +  +  +  - ]:          6 :             else if (depth > 0 && parent_id != ancestors[depth - 1]->id) {
     742         [ +  - ]:          2 :                 if (parent != NULL) {
     743                 :          2 :                     ts->rsp_pos += snprintf(ts->rsp + ts->rsp_pos, ts->rsp_size - ts->rsp_pos,
     744                 :            :                                             "\"%s\":{", parent->name);
     745                 :          2 :                     ancestors[depth++] = parent;
     746                 :            :                 }
     747                 :            :             }
     748                 :         10 :             ts->rsp_pos += ts->api->serialize_key_value(ts, &ts->data_objects[i]);
     749                 :            :         }
     750         [ -  + ]:        142 :         if (ts->rsp_pos >= ts->rsp_size - 1 - depth) {
     751                 :          0 :             return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     752                 :            :         }
     753                 :            :     }
     754                 :            : 
     755                 :          2 :     ts->rsp_pos--; /* overwrite internal comma */
     756                 :            : 
     757         [ +  + ]:          8 :     while (depth >= 0) {
     758                 :          6 :         ts->rsp[ts->rsp_pos++] = '}';
     759                 :          6 :         depth--;
     760                 :            :     }
     761                 :            : 
     762                 :          2 :     ts->rsp[ts->rsp_pos++] = ',';
     763                 :            : 
     764                 :          2 :     return 0;
     765                 :            : }
     766                 :            : 
     767                 :          4 : static int txt_serialize_report_header(struct thingset_context *ts, const char *path)
     768                 :            : {
     769                 :          4 :     ts->rsp_pos = snprintf(ts->rsp, ts->rsp_size, "#%s ", path);
     770         [ -  + ]:          4 :     if (ts->rsp_pos < 0 || ts->rsp_pos > ts->rsp_size) {
     771                 :          0 :         return -THINGSET_ERR_RESPONSE_TOO_LARGE;
     772                 :            :     }
     773                 :            :     else {
     774                 :          4 :         return 0;
     775                 :            :     }
     776                 :            : }
     777                 :            : 
     778                 :         11 : static void txt_deserialize_payload_reset(struct thingset_context *ts)
     779                 :            : {
     780                 :         11 :     ts->msg_pos = ts->msg_payload - ts->msg;
     781                 :            : 
     782                 :         11 :     txt_parse_payload(ts);
     783                 :         11 : }
     784                 :            : 
     785                 :          5 : static int txt_deserialize_string(struct thingset_context *ts, const char **str_start,
     786                 :            :                                   size_t *str_len)
     787                 :            : {
     788         [ +  - ]:          5 :     if (ts->tok_pos < ts->tok_count) {
     789         [ +  + ]:          5 :         if (ts->tokens[ts->tok_pos].type == JSMN_STRING) {
     790                 :          3 :             *str_start = ts->msg_payload + ts->tokens[ts->tok_pos].start;
     791                 :          3 :             *str_len = ts->tokens[ts->tok_pos].end - ts->tokens[ts->tok_pos].start;
     792                 :          3 :             ts->tok_pos++;
     793                 :          3 :             return 0;
     794                 :            :         }
     795                 :            :         else {
     796                 :          2 :             return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     797                 :            :         }
     798                 :            :     }
     799                 :            :     else {
     800                 :          0 :         return -THINGSET_ERR_BAD_REQUEST;
     801                 :            :     }
     802                 :            : }
     803                 :            : 
     804                 :         70 : static int txt_deserialize_child(struct thingset_context *ts,
     805                 :            :                                  const struct thingset_data_object **object)
     806                 :            : {
     807         [ +  + ]:         70 :     if (ts->tok_pos >= ts->tok_count) {
     808                 :         30 :         return -THINGSET_ERR_DESERIALIZATION_FINISHED;
     809                 :            :     }
     810                 :            : 
     811         [ +  + ]:         40 :     if (ts->tokens[ts->tok_pos].type != JSMN_STRING) {
     812                 :          1 :         return -THINGSET_ERR_BAD_REQUEST;
     813                 :            :     }
     814                 :            : 
     815                 :         39 :     const char *name = (char *)ts->msg_payload + ts->tokens[ts->tok_pos].start;
     816                 :         39 :     size_t name_len = ts->tokens[ts->tok_pos].end - ts->tokens[ts->tok_pos].start;
     817                 :            : 
     818         [ +  + ]:         39 :     if (ts->endpoint.object->id == THINGSET_ID_METADATA) {
     819                 :            :         int index;
     820                 :          1 :         *object = thingset_get_object_by_path(ts, name, name_len, &index);
     821                 :            :     }
     822                 :            :     else {
     823                 :         38 :         *object = thingset_get_child_by_name(ts, ts->endpoint.object->id, name, name_len);
     824                 :            :     }
     825                 :            : 
     826         [ +  + ]:         39 :     if (*object == NULL) {
     827                 :          2 :         return -THINGSET_ERR_NOT_FOUND;
     828                 :            :     }
     829                 :            : 
     830                 :         37 :     ts->tok_pos++;
     831                 :         37 :     return 0;
     832                 :            : }
     833                 :            : 
     834                 :         14 : static int txt_deserialize_null(struct thingset_context *ts)
     835                 :            : {
     836         [ +  - ]:         14 :     if (ts->tok_pos < ts->tok_count) {
     837                 :         14 :         jsmntok_t *token = &ts->tokens[ts->tok_pos];
     838         [ +  + ]:         14 :         if (token->type == JSMN_PRIMITIVE
     839         [ +  - ]:          2 :             && strncmp(ts->msg_payload + token->start, "null", token->end - token->start) == 0)
     840                 :            :         {
     841                 :          2 :             ts->tok_pos++;
     842                 :          2 :             return 0;
     843                 :            :         }
     844                 :            :         else {
     845                 :         12 :             return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     846                 :            :         }
     847                 :            :     }
     848                 :            :     else {
     849                 :          0 :         return -THINGSET_ERR_BAD_REQUEST;
     850                 :            :     }
     851                 :            : }
     852                 :            : 
     853                 :         27 : static int txt_deserialize_list_start(struct thingset_context *ts)
     854                 :            : {
     855         [ +  + ]:         27 :     if (ts->tok_pos < ts->tok_count) {
     856         [ +  + ]:         21 :         if (ts->tokens[ts->tok_pos].type == JSMN_ARRAY) {
     857                 :         20 :             ts->tok_pos++;
     858                 :         20 :             return 0;
     859                 :            :         }
     860                 :            :         else {
     861                 :          1 :             return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     862                 :            :         }
     863                 :            :     }
     864                 :            :     else {
     865                 :          6 :         return -THINGSET_ERR_BAD_REQUEST;
     866                 :            :     }
     867                 :            : }
     868                 :            : 
     869                 :         24 : static int txt_deserialize_map_start(struct thingset_context *ts)
     870                 :            : {
     871         [ +  - ]:         24 :     if (ts->tok_pos < ts->tok_count) {
     872         [ +  - ]:         24 :         if (ts->tokens[ts->tok_pos].type == JSMN_OBJECT) {
     873                 :         24 :             ts->tok_pos++;
     874                 :         24 :             return 0;
     875                 :            :         }
     876                 :            :         else {
     877                 :          0 :             return -THINGSET_ERR_UNSUPPORTED_FORMAT;
     878                 :            :         }
     879                 :            :     }
     880                 :            :     else {
     881                 :          0 :         return -THINGSET_ERR_BAD_REQUEST;
     882                 :            :     }
     883                 :            : }
     884                 :            : 
     885                 :          0 : static int txt_deserialize_skip(struct thingset_context *ts)
     886                 :            : {
     887         [ #  # ]:          0 :     if (ts->tok_pos < ts->tok_count) {
     888                 :          0 :         ts->tok_pos++;
     889                 :          0 :         return 0;
     890                 :            :     }
     891                 :            :     else {
     892                 :          0 :         return -THINGSET_ERR_BAD_REQUEST;
     893                 :            :     }
     894                 :            : }
     895                 :            : 
     896                 :         12 : static int txt_deserialize_finish(struct thingset_context *ts)
     897                 :            : {
     898         [ +  + ]:         12 :     return ts->tok_count == ts->tok_pos ? 0 : -THINGSET_ERR_BAD_REQUEST;
     899                 :            : }
     900                 :            : 
     901                 :            : static struct thingset_api txt_api = {
     902                 :            :     .serialize_response = txt_serialize_response,
     903                 :            :     .serialize_key = txt_serialize_name,
     904                 :            :     .serialize_value = txt_serialize_value,
     905                 :            :     .serialize_key_value = txt_serialize_name_value,
     906                 :            :     .serialize_path = txt_serialize_path,
     907                 :            : #ifdef CONFIG_THINGSET_METADATA_ENDPOINT
     908                 :            :     .serialize_metadata = txt_serialize_metadata,
     909                 :            : #endif
     910                 :            :     .serialize_map_start = txt_serialize_map_start,
     911                 :            :     .serialize_map_end = txt_serialize_map_end,
     912                 :            :     .serialize_list_start = txt_serialize_list_start,
     913                 :            :     .serialize_list_end = txt_serialize_list_end,
     914                 :            :     .serialize_subsets = txt_serialize_subsets,
     915                 :            :     .serialize_report_header = txt_serialize_report_header,
     916                 :            :     .serialize_finish = txt_serialize_finish,
     917                 :            :     .deserialize_payload_reset = txt_deserialize_payload_reset,
     918                 :            :     .deserialize_string = txt_deserialize_string,
     919                 :            :     .deserialize_null = txt_deserialize_null,
     920                 :            :     .deserialize_list_start = txt_deserialize_list_start,
     921                 :            :     .deserialize_map_start = txt_deserialize_map_start,
     922                 :            :     .deserialize_child = txt_deserialize_child,
     923                 :            :     .deserialize_value = txt_deserialize_value,
     924                 :            :     .deserialize_skip = txt_deserialize_skip,
     925                 :            :     .deserialize_finish = txt_deserialize_finish,
     926                 :            : };
     927                 :            : 
     928                 :         71 : inline void thingset_txt_setup(struct thingset_context *ts)
     929                 :            : {
     930                 :         71 :     ts->api = &txt_api;
     931                 :         71 : }
     932                 :            : 
     933                 :         65 : int thingset_txt_process(struct thingset_context *ts)
     934                 :            : {
     935                 :            :     int ret;
     936                 :            : 
     937                 :         65 :     thingset_txt_setup(ts);
     938                 :            : 
     939                 :            :     /* requests ordered with expected highest probability first */
     940                 :            :     int (*request_fn)(struct thingset_context *ts);
     941   [ +  +  +  +  :         65 :     switch (ts->msg[0]) {
                +  +  - ]
     942                 :         27 :         case THINGSET_TXT_GET_FETCH:
     943                 :         27 :             request_fn = thingset_txt_get_fetch;
     944                 :         27 :             break;
     945                 :         16 :         case THINGSET_TXT_UPDATE:
     946                 :         16 :             request_fn = thingset_common_update;
     947                 :         16 :             break;
     948                 :         12 :         case THINGSET_TXT_EXEC:
     949                 :         12 :             request_fn = thingset_common_exec;
     950                 :         12 :             break;
     951                 :          8 :         case THINGSET_TXT_CREATE:
     952                 :          8 :             request_fn = thingset_common_create;
     953                 :          8 :             break;
     954                 :          1 :         case THINGSET_TXT_DELETE:
     955                 :          1 :             request_fn = thingset_common_delete;
     956                 :          1 :             break;
     957                 :          1 :         case THINGSET_TXT_DESIRE:
     958                 :          1 :             request_fn = thingset_txt_desire;
     959                 :          1 :             break;
     960                 :          0 :         default:
     961                 :          0 :             return -THINGSET_ERR_BAD_REQUEST;
     962                 :            :     }
     963                 :            : 
     964                 :         65 :     ret = txt_parse_endpoint(ts);
     965         [ +  + ]:         65 :     if (ret != 0) {
     966                 :          3 :         ts->api->serialize_response(ts, -ret, "Invalid endpoint");
     967                 :          3 :         goto out;
     968                 :            :     }
     969                 :            : 
     970                 :         62 :     ret = txt_parse_payload(ts);
     971         [ +  + ]:         62 :     if (ret != 0) {
     972                 :          2 :         ts->api->serialize_response(ts, -ret, "JSON parsing error");
     973                 :          2 :         goto out;
     974                 :            :     }
     975                 :            : 
     976                 :         60 :     ret = request_fn(ts);
     977                 :            : 
     978                 :         65 : out:
     979         [ +  + ]:         65 :     if (ts->msg[0] != THINGSET_TXT_DESIRE) {
     980         [ +  - ]:         64 :         if (ts->rsp_pos > 0) {
     981                 :         64 :             ts->api->serialize_finish(ts);
     982                 :            :         }
     983                 :         64 :         return ts->rsp_pos;
     984                 :            :     }
     985                 :            :     else {
     986                 :          1 :         ts->rsp_pos = 0;
     987                 :          1 :         return ret;
     988                 :            :     }
     989                 :            : }

Generated by: LCOV version 1.14