Uniot Core
0.9.0
Loading...
Searching...
No Matches
CBORObject.h
Go to the documentation of this file.
1/*
2 * This is a part of the Uniot project.
3 * Copyright (C) 2016-2023 Uniot <contact@uniot.io>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include <Arduino.h>
22#include <Bytes.h>
23#include <Logger.h>
24#include <cn-cbor.h>
25
26#include <memory>
27
28namespace uniot {
41 friend class COSEMessage;
42
43 public:
44 class Array;
45
51 CBORObject(const CBORObject &) : mDirty(false) {
52 _create();
53 UNIOT_LOG_WARN("copy constructor is not implemented");
54 }
55
63 UNIOT_LOG_WARN("copy assignment operator is not implemented");
64 return *this;
65 }
66
72 : mpParentObject(nullptr),
73 mpMapNode(nullptr),
74 mDirty(false) {
75 mErr.err = CN_CBOR_NO_ERROR;
76 mErr.pos = 0;
77
78 // NOTE: In C++, when a constructor of a derived class is executing, the object is not yet of the derived class type;
79 // it's still of the base class type.
80 // This means that virtual functions do not behave polymorphically within constructors (and destructors).
81 // If `read` is overridden in a class derived from `CBORObject`,
82 // that overridden version will not be called in the `CBORObject` constructor.
83 read(buf);
84 }
85
90 CBORObject() : mDirty(false) {
91 _create();
92 }
93
98 virtual ~CBORObject() {
99 _clean();
100 }
101
106 cn_cbor_errback getLastError() {
107 return mErr;
108 }
109
110 bool hasError() {
111 return mErr.err != CN_CBOR_NO_ERROR;
112 }
113
120 bool hasKey(const char *key) const {
121 return cn_cbor_mapget_string(mpMapNode, key) != nullptr;
122 }
123
131 Array putArray(int key) {
132 auto existing = cn_cbor_mapget_int(mpMapNode, key);
133 if (existing && existing->type == CN_CBOR_ARRAY) {
134 return Array(this, existing);
135 } else {
136 auto newArray = cn_cbor_array_create(_errback());
137 if (newArray) {
138 if (cn_cbor_mapput_int(mpMapNode, key, newArray, _errback())) {
139 _markAsDirty(true);
140 return Array(this, newArray);
141 } else {
142 cn_cbor_free(newArray);
143 }
144 }
145 }
146 return Array(this, nullptr);
147 }
148
156 inline Array putArray(const char *key) {
157 auto existing = cn_cbor_mapget_string(mpMapNode, key);
158 if (existing && existing->type == CN_CBOR_ARRAY) {
159 return Array(this, existing);
160 } else {
161 auto newArray = cn_cbor_array_create(_errback());
162 if (newArray) {
163 if (cn_cbor_mapput_string(mpMapNode, key, newArray, _errback())) {
164 _markAsDirty(true);
165 return Array(this, newArray);
166 } else {
167 cn_cbor_free(newArray);
168 }
169 }
170 }
171 return Array(this, nullptr);
172 }
173
180 CBORObject &put(int key, int value) {
181 return put(key, static_cast<int64_t>(value));
182 }
183
190 CBORObject &put(int key, uint64_t value) {
191 return put(key, static_cast<int64_t>(value));
192 }
193
201 CBORObject &put(int key, int64_t value) {
202 bool updated = false;
203 auto existing = cn_cbor_mapget_int(mpMapNode, key);
204 if (existing) {
205 updated = cn_cbor_int_update(existing, value);
206 } else {
207 updated = cn_cbor_mapput_int(mpMapNode, key, cn_cbor_int_create(value, _errback()), _errback());
208 }
209 _markAsDirty(updated);
210 return *this;
211 }
212
220 CBORObject &put(int key, const char *value) {
221 bool updated = false;
222 auto existing = cn_cbor_mapget_int(mpMapNode, key);
223 if (existing) {
224 updated = cn_cbor_string_update(existing, value);
225 if (!updated) {
226 UNIOT_LOG_WARN_IF(_isPtrEqual(existing, value), "pointer to the same value is specified for '%d'", key);
227 }
228 } else {
229 updated = cn_cbor_mapput_int(mpMapNode, key, cn_cbor_string_create(value, _errback()), _errback());
230 }
231 _markAsDirty(updated);
232 return *this;
233 }
234
243 CBORObject &put(int key, const uint8_t *value, int size) {
244 bool updated = false;
245 auto existing = cn_cbor_mapget_int(mpMapNode, key);
246 if (existing) {
247 updated = cn_cbor_data_update(existing, value, size);
248 if (!updated) {
249 UNIOT_LOG_WARN_IF(_isPtrEqual(existing, value), "pointer to the same value is specified for '%d'", key);
250 }
251 } else {
252 updated = cn_cbor_mapput_int(mpMapNode, key, cn_cbor_data_create(value, size, _errback()), _errback());
253 }
254 _markAsDirty(updated);
255 return *this;
256 }
257
264 CBORObject &put(const char *key, int value) {
265 return put(key, static_cast<int64_t>(value));
266 }
267
274 CBORObject &put(const char *key, uint64_t value) {
275 return put(key, static_cast<int64_t>(value));
276 }
277
285 CBORObject &put(const char *key, int64_t value) {
286 bool updated = false;
287 auto existing = cn_cbor_mapget_string(mpMapNode, key);
288 if (existing) {
289 updated = cn_cbor_int_update(existing, value);
290 } else {
291 updated = cn_cbor_mapput_string(mpMapNode, key, cn_cbor_int_create(value, _errback()), _errback());
292 }
293 _markAsDirty(updated);
294 return *this;
295 }
296
304 CBORObject &put(const char *key, const char *value) {
305 bool updated = false;
306 auto existing = cn_cbor_mapget_string(mpMapNode, key);
307 if (existing) {
308 updated = cn_cbor_string_update(existing, value);
309 if (!updated) {
310 UNIOT_LOG_WARN_IF(_isPtrEqual(existing, value), "pointer to the same value is specified for '%s'", key);
311 }
312 } else {
313 updated = cn_cbor_mapput_string(mpMapNode, key, cn_cbor_string_create(value, _errback()), _errback());
314 }
315 _markAsDirty(updated);
316 return *this;
317 }
318
327 CBORObject &put(const char *key, const uint8_t *value, int size) {
328 bool updated = false;
329 auto existing = cn_cbor_mapget_string(mpMapNode, key);
330 if (existing) {
331 updated = cn_cbor_data_update(existing, value, size);
332 if (!updated) {
333 UNIOT_LOG_WARN_IF(_isPtrEqual(existing, value), "pointer to the same value is specified for '%s'", key);
334 }
335 } else {
336 updated = cn_cbor_mapput_string(mpMapNode, key, cn_cbor_data_create(value, size, _errback()), _errback());
337 }
338 _markAsDirty(updated);
339 return *this;
340 }
341
348 inline CBORObject putMap(const char *key) {
349 auto existing = cn_cbor_mapget_string(mpMapNode, key);
350 if (existing) {
351 return _getMap(existing);
352 }
353
354 auto newMap = cn_cbor_map_create(_errback());
355 auto success = cn_cbor_mapput_string(mpMapNode, key, newMap, _errback());
356 if (success) {
357 _markAsDirty(true);
358 return CBORObject(this, newMap);
359 }
360 return {};
361 }
362
369 inline CBORObject getMap(int key) {
370 return _getMap(cn_cbor_mapget_int(mpMapNode, key));
371 }
372
379 inline CBORObject getMap(const char *key) {
380 return _getMap(cn_cbor_mapget_string(mpMapNode, key));
381 }
382
389 bool getBool(int key) const {
390 return _getBool(cn_cbor_mapget_int(mpMapNode, key));
391 }
392
399 bool getBool(const char *key) const {
400 return _getBool(cn_cbor_mapget_string(mpMapNode, key));
401 }
402
409 long getInt(int key) const {
410 return _getInt(cn_cbor_mapget_int(mpMapNode, key));
411 }
412
419 long getInt(const char *key) const {
420 return _getInt(cn_cbor_mapget_string(mpMapNode, key));
421 }
422
429 String getString(int key) const {
430 return _getString(cn_cbor_mapget_int(mpMapNode, key));
431 }
432
439 String getString(const char *key) const {
440 return _getString(cn_cbor_mapget_string(mpMapNode, key));
441 }
442
450 String getValueAsString(int key) const {
451 return _getValueAsString(cn_cbor_mapget_int(mpMapNode, key));
452 }
453
461 String getValueAsString(const char *key) const {
462 return _getValueAsString(cn_cbor_mapget_string(mpMapNode, key));
463 }
464
471 Bytes getBytes(int key) const {
472 return _getBytes(cn_cbor_mapget_int(mpMapNode, key));
473 }
474
481 Bytes getBytes(const char *key) const {
482 return _getBytes(cn_cbor_mapget_string(mpMapNode, key));
483 }
484
490 void read(const Bytes &buf) {
491 if (mpParentObject) {
492 UNIOT_LOG_WARN("the parent node is not null, the object is not read");
493 return;
494 }
495
496 _clean();
497 mBuf = buf;
498 mpMapNode = cn_cbor_decode(mBuf.raw(), mBuf.size(), _errback());
499 if (!mpMapNode) {
500 _create();
501 }
502 }
503
509 Bytes build() const {
510 auto visitSiblings = mpParentObject == nullptr;
511 return _build(mpMapNode, visitSiblings);
512 }
513
519 bool isChild() const {
520 return mpParentObject != nullptr;
521 }
522
528 bool dirty() const {
529 return mDirty;
530 }
531
536 void forceDirty() {
537 UNIOT_LOG_DEBUG("data marked dirty by force");
538 _markAsDirty(true);
539 }
540
545 void clean() {
546 _clean();
547 _create();
548 }
549
556 class Array {
557 friend class CBORObject;
558
559 public:
564 Array(const Array &other)
565 : mpContext(other.mpContext),
566 mpArrayNode(other.mpArrayNode) {
567 }
568
574 Array &operator=(const Array &other) {
575 if (this != &other) {
576 mpContext = other.mpContext;
577 mpArrayNode = other.mpArrayNode;
578 }
579 return *this;
580 }
581
586 mpContext = nullptr;
587 mpArrayNode = nullptr;
588 }
589
594 cn_cbor_errback getLastError() {
595 return mpContext->mErr;
596 }
597
603 inline Array &append(int value) {
604 if (mpArrayNode) {
605 auto updated = cn_cbor_array_append(mpArrayNode, cn_cbor_int_create(value, mpContext->_errback()), mpContext->_errback());
606 mpContext->_markAsDirty(updated);
607 }
608 return *this;
609 }
610
616 inline Array &append(const char *value) {
617 if (mpArrayNode) {
618 auto updated = cn_cbor_array_append(mpArrayNode, cn_cbor_string_create(value, mpContext->_errback()), mpContext->_errback());
619 mpContext->_markAsDirty(updated);
620 }
621 return *this;
622 }
623
631 template <typename T>
632 inline Array &append(size_t size, const T *value) {
633 static_assert(std::is_integral<T>::value, "only integral types are allowed");
634
635 for (size_t i = 0; i < size; i++) {
636 append(static_cast<int>(value[i]));
637 }
638 return *this;
639 }
640
646 auto newArray = cn_cbor_array_create(mpContext->_errback());
647 if (newArray) {
648 auto updated = cn_cbor_array_append(mpArrayNode, newArray, mpContext->_errback());
649 mpContext->_markAsDirty(updated);
650 if (updated) {
651 return Array(mpContext, newArray);
652 } else {
653 cn_cbor_free(newArray);
654 }
655 }
656 return Array(mpContext, nullptr);
657 }
658
659 private:
665 Array(CBORObject *context, cn_cbor *arrayNode)
666 : mpContext(context), mpArrayNode(arrayNode) {}
667
668 CBORObject *mpContext;
669 cn_cbor *mpArrayNode;
670 };
671
672 private:
678 CBORObject(CBORObject *parent, cn_cbor *child) : mDirty(false) {
679 mErr.err = CN_CBOR_NO_ERROR;
680 mErr.pos = 0;
681 mpMapNode = child;
682 mpParentObject = parent;
683 }
684
689 void _create() {
690 mErr.err = CN_CBOR_NO_ERROR;
691 mErr.pos = 0;
692 mDirty = false;
693 mpMapNode = cn_cbor_map_create(_errback());
694 mpParentObject = nullptr;
695 }
696
701 void _clean() {
702 if (!mpParentObject) {
703 if (mpMapNode) {
704 cn_cbor_free(mpMapNode);
705 }
706 }
707 mpMapNode = nullptr;
708 mpParentObject = nullptr;
709 mDirty = false;
710 mErr.err = CN_CBOR_NO_ERROR;
711 mErr.pos = 0;
712 mBuf.clean();
713 }
714
721 Bytes _build(cn_cbor *cb, bool visitSiblings = true) const {
722 auto calculated = cn_cbor_encoder_write(NULL, 0, 0, cb, visitSiblings);
723 Bytes bytes(nullptr, calculated);
724 auto written = bytes.fill([&](uint8_t *buf, size_t size) {
725 auto actual = cn_cbor_encoder_write(buf, 0, size, cb, visitSiblings);
726 if (actual < 0) {
727 UNIOT_LOG_ERROR("%s", "CBORObject build failed, buffer size too small");
728 return 0;
729 }
730 return actual;
731 });
732
733 bytes.prune(written);
734 return bytes;
735 }
736
743 inline CBORObject _getMap(cn_cbor *cb) {
744 // if(!cb) throw "error"; // TODO: ???
745 if (cb && CN_CBOR_MAP == cb->type) {
746 return CBORObject(this, cb);
747 }
748
749 UNIOT_LOG_WARN("the map is not found");
750 return CBORObject();
751 }
752
759 long _getBool(cn_cbor *cb) const {
760 // if(!cb) throw "error"; // TODO: ???
761 if (cb) {
762 if (CN_CBOR_TRUE == cb->type) {
763 return true;
764 }
765 if (CN_CBOR_FALSE == cb->type) {
766 return false;
767 }
768 }
769 return false;
770 }
771
778 long _getInt(cn_cbor *cb) const {
779 // if(!cb) throw "error"; // TODO: ???
780 if (cb && CN_CBOR_INT == cb->type) {
781 return cb->v.sint;
782 } else if (cb && CN_CBOR_UINT == cb->type) {
783 return cb->v.uint;
784 }
785 return 0;
786 }
787
794 String _getString(cn_cbor *cb) const {
795 // if(!cb) throw "error"; // TODO: ???
796 if (cb && CN_CBOR_TEXT == cb->type) {
797 auto bytes = Bytes(cb->v.bytes, cb->length);
798 bytes.terminate();
799 return String(bytes.c_str());
800 }
801 return "";
802 }
803
810 Bytes _getBytes(cn_cbor *cb) const {
811 // if(!cb) throw "error"; // TODO: ???
812 if (cb && CN_CBOR_BYTES == cb->type) {
813 return Bytes(cb->v.bytes, cb->length);
814 }
815 return {};
816 }
817
824 String _getValueAsString(cn_cbor *cb) const {
825 // if(!cb) throw "error"; // TODO: ???
826 if (cb) {
827 if (CN_CBOR_TEXT == cb->type) {
828 auto bytes = Bytes(cb->v.bytes, cb->length);
829 bytes.terminate();
830 return String(bytes.c_str());
831 }
832 if (CN_CBOR_INT == cb->type) {
833 return String(cb->v.sint);
834 }
835 if (CN_CBOR_UINT == cb->type) {
836 return String(cb->v.uint);
837 }
838 if (CN_CBOR_FLOAT == cb->type) {
839 return String(cb->v.f);
840 }
841 if (CN_CBOR_DOUBLE == cb->type) {
842 return String(cb->v.dbl);
843 }
844 if (CN_CBOR_TRUE == cb->type) {
845 return String("1");
846 }
847 if (CN_CBOR_FALSE == cb->type) {
848 return String("0");
849 }
850 }
851 return "";
852 }
853
861 bool _isPtrEqual(cn_cbor *cb, const void *ptr) const {
862 return ptr == (const void *)cb->v.bytes;
863 }
864
870 void _markAsDirty(bool updated) {
871 if (updated) {
872 mDirty = true;
873 if (mpParentObject) {
874 mpParentObject->_markAsDirty(true);
875 }
876 }
877 }
878
884 cn_cbor_errback *_errback() {
885 UNIOT_LOG_ERROR_IF(mErr.err, "last unhandled error code: %lu", mErr.err);
886
887 mErr.err = CN_CBOR_NO_ERROR;
888 mErr.pos = 0;
889 return &mErr;
890 }
891
892 Bytes mBuf;
893 CBORObject *mpParentObject;
894 cn_cbor *mpMapNode;
895 cn_cbor_errback mErr;
896 bool mDirty;
897};
898
899} // namespace uniot
Definition Bytes.h:38
Helper class for working with CBOR arrays.
Definition CBORObject.h:556
~Array()
Destructor.
Definition CBORObject.h:585
cn_cbor_errback getLastError()
Get the last error that occurred during array operations.
Definition CBORObject.h:594
friend class CBORObject
Definition CBORObject.h:557
Array(const Array &other)
Copy constructor.
Definition CBORObject.h:564
Array & append(const char *value)
Append a string to the array.
Definition CBORObject.h:616
Array & operator=(const Array &other)
Copy assignment operator.
Definition CBORObject.h:574
Array & append(int value)
Append an integer to the array.
Definition CBORObject.h:603
Array appendArray()
Append a new array as an element.
Definition CBORObject.h:645
Array & append(size_t size, const T *value)
Append multiple values from an array to the CBOR array.
Definition CBORObject.h:632
Definition CBORObject.h:40
CBORObject getMap(const char *key)
Get a map at a specific string key.
Definition CBORObject.h:379
friend class COSEMessage
Definition CBORObject.h:41
bool hasKey(const char *key) const
Check if the map contains a specific string key.
Definition CBORObject.h:120
bool dirty() const
Check if the object has been modified since creation or last read.
Definition CBORObject.h:528
long getInt(int key) const
Get an integer value at a specific integer key.
Definition CBORObject.h:409
CBORObject(const CBORObject &)
Copy constructor (not implemented)
Definition CBORObject.h:51
Bytes getBytes(int key) const
Get binary data at a specific integer key.
Definition CBORObject.h:471
long getInt(const char *key) const
Get an integer value at a specific string key.
Definition CBORObject.h:419
Array putArray(int key)
Create or get an array at a specific integer key.
Definition CBORObject.h:131
CBORObject & operator=(const CBORObject &)
Copy assignment operator (not implemented)
Definition CBORObject.h:62
CBORObject & put(int key, int64_t value)
Put a 64-bit integer value at a specific integer key.
Definition CBORObject.h:201
CBORObject(Bytes buf)
Construct a CBORObject from binary CBOR data.
Definition CBORObject.h:71
CBORObject putMap(const char *key)
Put a new map at a specific string key, or get the existing one.
Definition CBORObject.h:348
String getValueAsString(const char *key) const
Get a value as a string at a specific string key.
Definition CBORObject.h:461
CBORObject()
Construct an empty CBORObject.
Definition CBORObject.h:90
CBORObject & put(const char *key, int value)
Put an integer value at a specific string key.
Definition CBORObject.h:264
String getString(const char *key) const
Get a string value at a specific string key.
Definition CBORObject.h:439
CBORObject & put(int key, const uint8_t *value, int size)
Put binary data at a specific integer key.
Definition CBORObject.h:243
CBORObject & put(int key, int value)
Put an integer value at a specific integer key.
Definition CBORObject.h:180
void forceDirty()
Force the object to be marked as dirty (modified)
Definition CBORObject.h:536
void read(const Bytes &buf)
Read CBOR data from a buffer.
Definition CBORObject.h:490
CBORObject & put(int key, uint64_t value)
Put an unsigned 64-bit integer value at a specific integer key.
Definition CBORObject.h:190
CBORObject & put(const char *key, uint64_t value)
Put an unsigned 64-bit integer value at a specific string key.
Definition CBORObject.h:274
String getString(int key) const
Get a string value at a specific integer key.
Definition CBORObject.h:429
CBORObject getMap(int key)
Get a map at a specific integer key.
Definition CBORObject.h:369
void clean()
Reset the object to an empty state.
Definition CBORObject.h:545
CBORObject & put(const char *key, const uint8_t *value, int size)
Put binary data at a specific string key.
Definition CBORObject.h:327
bool hasError()
Definition CBORObject.h:110
bool getBool(const char *key) const
Get a boolean value at a specific string key.
Definition CBORObject.h:399
cn_cbor_errback getLastError()
Get the last error that occurred during CBOR operations.
Definition CBORObject.h:106
CBORObject & put(int key, const char *value)
Put a string value at a specific integer key.
Definition CBORObject.h:220
CBORObject & put(const char *key, int64_t value)
Put a 64-bit integer value at a specific string key.
Definition CBORObject.h:285
virtual ~CBORObject()
Virtual destructor.
Definition CBORObject.h:98
Bytes getBytes(const char *key) const
Get binary data at a specific string key.
Definition CBORObject.h:481
Array putArray(const char *key)
Create or get an array at a specific string key.
Definition CBORObject.h:156
CBORObject & put(const char *key, const char *value)
Put a string value at a specific string key.
Definition CBORObject.h:304
bool isChild() const
Check if this object is a child node in a CBOR tree.
Definition CBORObject.h:519
Bytes build() const
Build the CBOR data into binary format.
Definition CBORObject.h:509
bool getBool(int key) const
Get a boolean value at a specific integer key.
Definition CBORObject.h:389
String getValueAsString(int key) const
Get a value as a string at a specific integer key.
Definition CBORObject.h:450
#define UNIOT_LOG_WARN(...)
Log an WARN level message Used for warnings about potentially problematic situations....
Definition Logger.h:294
#define UNIOT_LOG_WARN_IF(log_cond, log...)
Conditionally log an WARN level message Used for warnings about potentially problematic situations....
Definition Logger.h:302
#define UNIOT_LOG_DEBUG(...)
Log an DEBUG level message Used for general information about system operation. Only compiled if UNIO...
Definition Logger.h:340
#define UNIOT_LOG_ERROR_IF(log_cond, log...)
Conditionally log an ERROR level message Used for critical errors that may prevent normal operation....
Definition Logger.h:281
#define UNIOT_LOG_ERROR(...)
Log an ERROR level message Used for critical errors that may prevent normal operation....
Definition Logger.h:273
Contains all classes and functions related to the Uniot Core.