Uniot Core
0.8.1
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
121 Array putArray(int key) {
122 auto existing = cn_cbor_mapget_int(mpMapNode, key);
123 if (existing && existing->type == CN_CBOR_ARRAY) {
124 return Array(this, existing);
125 } else {
126 auto newArray = cn_cbor_array_create(_errback());
127 if (newArray) {
128 if (cn_cbor_mapput_int(mpMapNode, key, newArray, _errback())) {
129 _markAsDirty(true);
130 return Array(this, newArray);
131 } else {
132 cn_cbor_free(newArray);
133 }
134 }
135 }
136 return Array(this, nullptr);
137 }
138
146 inline Array putArray(const char *key) {
147 auto existing = cn_cbor_mapget_string(mpMapNode, key);
148 if (existing && existing->type == CN_CBOR_ARRAY) {
149 return Array(this, existing);
150 } else {
151 auto newArray = cn_cbor_array_create(_errback());
152 if (newArray) {
153 if (cn_cbor_mapput_string(mpMapNode, key, newArray, _errback())) {
154 _markAsDirty(true);
155 return Array(this, newArray);
156 } else {
157 cn_cbor_free(newArray);
158 }
159 }
160 }
161 return Array(this, nullptr);
162 }
163
170 CBORObject &put(int key, int value) {
171 return put(key, static_cast<int64_t>(value));
172 }
173
180 CBORObject &put(int key, uint64_t value) {
181 return put(key, static_cast<int64_t>(value));
182 }
183
191 CBORObject &put(int key, int64_t value) {
192 bool updated = false;
193 auto existing = cn_cbor_mapget_int(mpMapNode, key);
194 if (existing) {
195 updated = cn_cbor_int_update(existing, value);
196 } else {
197 updated = cn_cbor_mapput_int(mpMapNode, key, cn_cbor_int_create(value, _errback()), _errback());
198 }
199 _markAsDirty(updated);
200 return *this;
201 }
202
210 CBORObject &put(int key, const char *value) {
211 bool updated = false;
212 auto existing = cn_cbor_mapget_int(mpMapNode, key);
213 if (existing) {
214 updated = cn_cbor_string_update(existing, value);
215 if (!updated) {
216 UNIOT_LOG_WARN_IF(_isPtrEqual(existing, value), "pointer to the same value is specified for '%d'", key);
217 }
218 } else {
219 updated = cn_cbor_mapput_int(mpMapNode, key, cn_cbor_string_create(value, _errback()), _errback());
220 }
221 _markAsDirty(updated);
222 return *this;
223 }
224
233 CBORObject &put(int key, const uint8_t *value, int size) {
234 bool updated = false;
235 auto existing = cn_cbor_mapget_int(mpMapNode, key);
236 if (existing) {
237 updated = cn_cbor_data_update(existing, value, size);
238 if (!updated) {
239 UNIOT_LOG_WARN_IF(_isPtrEqual(existing, value), "pointer to the same value is specified for '%d'", key);
240 }
241 } else {
242 updated = cn_cbor_mapput_int(mpMapNode, key, cn_cbor_data_create(value, size, _errback()), _errback());
243 }
244 _markAsDirty(updated);
245 return *this;
246 }
247
254 CBORObject &put(const char *key, int value) {
255 return put(key, static_cast<int64_t>(value));
256 }
257
264 CBORObject &put(const char *key, uint64_t value) {
265 return put(key, static_cast<int64_t>(value));
266 }
267
275 CBORObject &put(const char *key, int64_t value) {
276 bool updated = false;
277 auto existing = cn_cbor_mapget_string(mpMapNode, key);
278 if (existing) {
279 updated = cn_cbor_int_update(existing, value);
280 } else {
281 updated = cn_cbor_mapput_string(mpMapNode, key, cn_cbor_int_create(value, _errback()), _errback());
282 }
283 _markAsDirty(updated);
284 return *this;
285 }
286
294 CBORObject &put(const char *key, const char *value) {
295 bool updated = false;
296 auto existing = cn_cbor_mapget_string(mpMapNode, key);
297 if (existing) {
298 updated = cn_cbor_string_update(existing, value);
299 if (!updated) {
300 UNIOT_LOG_WARN_IF(_isPtrEqual(existing, value), "pointer to the same value is specified for '%s'", key);
301 }
302 } else {
303 updated = cn_cbor_mapput_string(mpMapNode, key, cn_cbor_string_create(value, _errback()), _errback());
304 }
305 _markAsDirty(updated);
306 return *this;
307 }
308
317 CBORObject &put(const char *key, const uint8_t *value, int size) {
318 bool updated = false;
319 auto existing = cn_cbor_mapget_string(mpMapNode, key);
320 if (existing) {
321 updated = cn_cbor_data_update(existing, value, size);
322 if (!updated) {
323 UNIOT_LOG_WARN_IF(_isPtrEqual(existing, value), "pointer to the same value is specified for '%s'", key);
324 }
325 } else {
326 updated = cn_cbor_mapput_string(mpMapNode, key, cn_cbor_data_create(value, size, _errback()), _errback());
327 }
328 _markAsDirty(updated);
329 return *this;
330 }
331
338 inline CBORObject putMap(const char *key) {
339 auto existing = cn_cbor_mapget_string(mpMapNode, key);
340 if (existing) {
341 return _getMap(existing);
342 }
343
344 auto newMap = cn_cbor_map_create(_errback());
345 auto success = cn_cbor_mapput_string(mpMapNode, key, newMap, _errback());
346 if (success) {
347 _markAsDirty(true);
348 return CBORObject(this, newMap);
349 }
350 return {};
351 }
352
359 inline CBORObject getMap(int key) {
360 return _getMap(cn_cbor_mapget_int(mpMapNode, key));
361 }
362
369 inline CBORObject getMap(const char *key) {
370 return _getMap(cn_cbor_mapget_string(mpMapNode, key));
371 }
372
379 bool getBool(int key) const {
380 return _getBool(cn_cbor_mapget_int(mpMapNode, key));
381 }
382
389 bool getBool(const char *key) const {
390 return _getBool(cn_cbor_mapget_string(mpMapNode, key));
391 }
392
399 long getInt(int key) const {
400 return _getInt(cn_cbor_mapget_int(mpMapNode, key));
401 }
402
409 long getInt(const char *key) const {
410 return _getInt(cn_cbor_mapget_string(mpMapNode, key));
411 }
412
419 String getString(int key) const {
420 return _getString(cn_cbor_mapget_int(mpMapNode, key));
421 }
422
429 String getString(const char *key) const {
430 return _getString(cn_cbor_mapget_string(mpMapNode, key));
431 }
432
440 String getValueAsString(int key) const {
441 return _getValueAsString(cn_cbor_mapget_int(mpMapNode, key));
442 }
443
451 String getValueAsString(const char *key) const {
452 return _getValueAsString(cn_cbor_mapget_string(mpMapNode, key));
453 }
454
461 Bytes getBytes(int key) const {
462 return _getBytes(cn_cbor_mapget_int(mpMapNode, key));
463 }
464
471 Bytes getBytes(const char *key) const {
472 return _getBytes(cn_cbor_mapget_string(mpMapNode, key));
473 }
474
480 void read(const Bytes &buf) {
481 if (mpParentObject) {
482 UNIOT_LOG_WARN("the parent node is not null, the object is not read");
483 return;
484 }
485
486 _clean();
487 mBuf = buf;
488 mpMapNode = cn_cbor_decode(mBuf.raw(), mBuf.size(), _errback());
489 if (!mpMapNode) {
490 _create();
491 }
492 }
493
499 Bytes build() const {
500 auto visitSiblings = mpParentObject == nullptr;
501 return _build(mpMapNode, visitSiblings);
502 }
503
509 bool isChild() const {
510 return mpParentObject != nullptr;
511 }
512
518 bool dirty() const {
519 return mDirty;
520 }
521
526 void forceDirty() {
527 UNIOT_LOG_WARN("the data forced marked as dirty");
528 _markAsDirty(true);
529 }
530
535 void clean() {
536 _clean();
537 _create();
538 }
539
546 class Array {
547 friend class CBORObject;
548
549 public:
554 Array(const Array &other)
555 : mpContext(other.mpContext),
556 mpArrayNode(other.mpArrayNode) {
557 }
558
564 Array &operator=(const Array &other) {
565 if (this != &other) {
566 mpContext = other.mpContext;
567 mpArrayNode = other.mpArrayNode;
568 }
569 return *this;
570 }
571
576 mpContext = nullptr;
577 mpArrayNode = nullptr;
578 }
579
584 cn_cbor_errback getLastError() {
585 return mpContext->mErr;
586 }
587
593 inline Array &append(int value) {
594 if (mpArrayNode) {
595 auto updated = cn_cbor_array_append(mpArrayNode, cn_cbor_int_create(value, mpContext->_errback()), mpContext->_errback());
596 mpContext->_markAsDirty(updated);
597 }
598 return *this;
599 }
600
606 inline Array &append(const char *value) {
607 if (mpArrayNode) {
608 auto updated = cn_cbor_array_append(mpArrayNode, cn_cbor_string_create(value, mpContext->_errback()), mpContext->_errback());
609 mpContext->_markAsDirty(updated);
610 }
611 return *this;
612 }
613
621 template <typename T>
622 inline Array &append(size_t size, const T *value) {
623 static_assert(std::is_integral<T>::value, "only integral types are allowed");
624
625 for (size_t i = 0; i < size; i++) {
626 append(static_cast<int>(value[i]));
627 }
628 return *this;
629 }
630
636 auto newArray = cn_cbor_array_create(mpContext->_errback());
637 if (newArray) {
638 auto updated = cn_cbor_array_append(mpArrayNode, newArray, mpContext->_errback());
639 mpContext->_markAsDirty(updated);
640 if (updated) {
641 return Array(mpContext, newArray);
642 } else {
643 cn_cbor_free(newArray);
644 }
645 }
646 return Array(mpContext, nullptr);
647 }
648
649 private:
655 Array(CBORObject *context, cn_cbor *arrayNode)
656 : mpContext(context), mpArrayNode(arrayNode) {}
657
658 CBORObject *mpContext;
659 cn_cbor *mpArrayNode;
660 };
661
662 private:
668 CBORObject(CBORObject *parent, cn_cbor *child) : mDirty(false) {
669 mErr.err = CN_CBOR_NO_ERROR;
670 mErr.pos = 0;
671 mpMapNode = child;
672 mpParentObject = parent;
673 }
674
679 void _create() {
680 mErr.err = CN_CBOR_NO_ERROR;
681 mErr.pos = 0;
682 mDirty = false;
683 mpMapNode = cn_cbor_map_create(_errback());
684 mpParentObject = nullptr;
685 }
686
691 void _clean() {
692 if (!mpParentObject) {
693 if (mpMapNode) {
694 cn_cbor_free(mpMapNode);
695 }
696 }
697 mpMapNode = nullptr;
698 mpParentObject = nullptr;
699 mDirty = false;
700 mErr.err = CN_CBOR_NO_ERROR;
701 mErr.pos = 0;
702 mBuf.clean();
703 }
704
711 Bytes _build(cn_cbor *cb, bool visitSiblings = true) const {
712 auto calculated = cn_cbor_encoder_write(NULL, 0, 0, cb, visitSiblings);
713 Bytes bytes(nullptr, calculated);
714 auto written = bytes.fill([&](uint8_t *buf, size_t size) {
715 auto actual = cn_cbor_encoder_write(buf, 0, size, cb, visitSiblings);
716 if (actual < 0) {
717 UNIOT_LOG_ERROR("%s", "CBORObject build failed, buffer size too small");
718 return 0;
719 }
720 return actual;
721 });
722
723 return bytes.prune(written);
724 }
725
732 inline CBORObject _getMap(cn_cbor *cb) {
733 // if(!cb) throw "error"; // TODO: ???
734 if (cb && CN_CBOR_MAP == cb->type) {
735 return CBORObject(this, cb);
736 }
737
738 UNIOT_LOG_WARN("the map is not found");
739 return CBORObject();
740 }
741
748 long _getBool(cn_cbor *cb) const {
749 // if(!cb) throw "error"; // TODO: ???
750 if (cb) {
751 if (CN_CBOR_TRUE == cb->type) {
752 return true;
753 }
754 if (CN_CBOR_FALSE == cb->type) {
755 return false;
756 }
757 }
758 return false;
759 }
760
767 long _getInt(cn_cbor *cb) const {
768 // if(!cb) throw "error"; // TODO: ???
769 if (cb && CN_CBOR_INT == cb->type) {
770 return cb->v.sint;
771 } else if (cb && CN_CBOR_UINT == cb->type) {
772 return cb->v.uint;
773 }
774 return 0;
775 }
776
783 String _getString(cn_cbor *cb) const {
784 // if(!cb) throw "error"; // TODO: ???
785 if (cb && CN_CBOR_TEXT == cb->type) {
786 auto bytes = Bytes(cb->v.bytes, cb->length);
787 bytes.terminate();
788 return String(bytes.c_str());
789 }
790 return "";
791 }
792
799 Bytes _getBytes(cn_cbor *cb) const {
800 // if(!cb) throw "error"; // TODO: ???
801 if (cb && CN_CBOR_BYTES == cb->type) {
802 return Bytes(cb->v.bytes, cb->length);
803 }
804 return {};
805 }
806
813 String _getValueAsString(cn_cbor *cb) const {
814 // if(!cb) throw "error"; // TODO: ???
815 if (cb) {
816 if (CN_CBOR_TEXT == cb->type) {
817 auto bytes = Bytes(cb->v.bytes, cb->length);
818 bytes.terminate();
819 return String(bytes.c_str());
820 }
821 if (CN_CBOR_INT == cb->type) {
822 return String(cb->v.sint);
823 }
824 if (CN_CBOR_UINT == cb->type) {
825 return String(cb->v.uint);
826 }
827 if (CN_CBOR_FLOAT == cb->type) {
828 return String(cb->v.f);
829 }
830 if (CN_CBOR_DOUBLE == cb->type) {
831 return String(cb->v.dbl);
832 }
833 if (CN_CBOR_TRUE == cb->type) {
834 return String("1");
835 }
836 if (CN_CBOR_FALSE == cb->type) {
837 return String("0");
838 }
839 }
840 return "";
841 }
842
850 bool _isPtrEqual(cn_cbor *cb, const void *ptr) const {
851 return ptr == (const void *)cb->v.bytes;
852 }
853
859 void _markAsDirty(bool updated) {
860 if (updated) {
861 mDirty = true;
862 if (mpParentObject) {
863 mpParentObject->_markAsDirty(true);
864 }
865 }
866 }
867
873 cn_cbor_errback *_errback() {
874 UNIOT_LOG_ERROR_IF(mErr.err, "last unhandled error code: %lu", mErr.err);
875
876 mErr.err = CN_CBOR_NO_ERROR;
877 mErr.pos = 0;
878 return &mErr;
879 }
880
881 Bytes mBuf;
882 CBORObject *mpParentObject;
883 cn_cbor *mpMapNode;
884 cn_cbor_errback mErr;
885 bool mDirty;
886};
887
888} // namespace uniot
Definition Bytes.h:38
Helper class for working with CBOR arrays.
Definition CBORObject.h:546
~Array()
Destructor.
Definition CBORObject.h:575
cn_cbor_errback getLastError()
Get the last error that occurred during array operations.
Definition CBORObject.h:584
friend class CBORObject
Definition CBORObject.h:547
Array(const Array &other)
Copy constructor.
Definition CBORObject.h:554
Array & append(const char *value)
Append a string to the array.
Definition CBORObject.h:606
Array & operator=(const Array &other)
Copy assignment operator.
Definition CBORObject.h:564
Array & append(int value)
Append an integer to the array.
Definition CBORObject.h:593
Array appendArray()
Append a new array as an element.
Definition CBORObject.h:635
Array & append(size_t size, const T *value)
Append multiple values from an array to the CBOR array.
Definition CBORObject.h:622
Definition CBORObject.h:40
CBORObject getMap(const char *key)
Get a map at a specific string key.
Definition CBORObject.h:369
friend class COSEMessage
Definition CBORObject.h:41
bool dirty() const
Check if the object has been modified since creation or last read.
Definition CBORObject.h:518
long getInt(int key) const
Get an integer value at a specific integer key.
Definition CBORObject.h:399
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:461
long getInt(const char *key) const
Get an integer value at a specific string key.
Definition CBORObject.h:409
Array putArray(int key)
Create or get an array at a specific integer key.
Definition CBORObject.h:121
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:191
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:338
String getValueAsString(const char *key) const
Get a value as a string at a specific string key.
Definition CBORObject.h:451
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:254
String getString(const char *key) const
Get a string value at a specific string key.
Definition CBORObject.h:429
CBORObject & put(int key, const uint8_t *value, int size)
Put binary data at a specific integer key.
Definition CBORObject.h:233
CBORObject & put(int key, int value)
Put an integer value at a specific integer key.
Definition CBORObject.h:170
void forceDirty()
Force the object to be marked as dirty (modified)
Definition CBORObject.h:526
void read(const Bytes &buf)
Read CBOR data from a buffer.
Definition CBORObject.h:480
CBORObject & put(int key, uint64_t value)
Put an unsigned 64-bit integer value at a specific integer key.
Definition CBORObject.h:180
CBORObject & put(const char *key, uint64_t value)
Put an unsigned 64-bit integer value at a specific string key.
Definition CBORObject.h:264
String getString(int key) const
Get a string value at a specific integer key.
Definition CBORObject.h:419
CBORObject getMap(int key)
Get a map at a specific integer key.
Definition CBORObject.h:359
void clean()
Reset the object to an empty state.
Definition CBORObject.h:535
CBORObject & put(const char *key, const uint8_t *value, int size)
Put binary data at a specific string key.
Definition CBORObject.h:317
bool hasError()
Definition CBORObject.h:110
bool getBool(const char *key) const
Get a boolean value at a specific string key.
Definition CBORObject.h:389
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:210
CBORObject & put(const char *key, int64_t value)
Put a 64-bit integer value at a specific string key.
Definition CBORObject.h:275
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:471
Array putArray(const char *key)
Create or get an array at a specific string key.
Definition CBORObject.h:146
CBORObject & put(const char *key, const char *value)
Put a string value at a specific string key.
Definition CBORObject.h:294
bool isChild() const
Check if this object is a child node in a CBOR tree.
Definition CBORObject.h:509
Bytes build() const
Build the CBOR data into binary format.
Definition CBORObject.h:499
bool getBool(int key) const
Get a boolean value at a specific integer key.
Definition CBORObject.h:379
String getValueAsString(int key) const
Get a value as a string at a specific integer key.
Definition CBORObject.h:440
#define UNIOT_LOG_WARN(...)
Log an WARN level message Used for warnings about potentially problematic situations....
Definition Logger.h:247
#define UNIOT_LOG_WARN_IF(log_cond, log...)
Conditionally log an WARN level message Used for warnings about potentially problematic situations....
Definition Logger.h:255
#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:234
#define UNIOT_LOG_ERROR(...)
Log an ERROR level message Used for critical errors that may prevent normal operation....
Definition Logger.h:226
Contains all classes and functions related to the Uniot Core.