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 : : }
|