Skip to content

Commit 66db68f

Browse files
implementing encoder interface
1 parent 6dd1bd7 commit 66db68f

File tree

2 files changed

+212
-0
lines changed

2 files changed

+212
-0
lines changed

src/cbor/CborEncoder.cpp

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
This file is part of the Arduino_CloudUtils library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
#include "CborEncoder.h"
11+
#include <algorithm>
12+
13+
Encoder::Status CBORMessageEncoderSingleton::encode(Message* message, uint8_t * data, size_t& len) { // TODO do we need to propagate the maximum length?
14+
// prepare cbor structure
15+
CborEncoder encoder;
16+
17+
cbor_encoder_init(&encoder, data, len, 0);
18+
19+
auto encoder_it = encoders.begin();
20+
21+
for(; encoder_it != encoders.end(); encoder_it++) {
22+
if(encoder_it->first == message->id) {
23+
break;
24+
}
25+
}
26+
27+
// check if message.id exists on the encoders list or return error
28+
if(encoder_it == encoders.end()) {
29+
return Encoder::Status::Error;
30+
}
31+
32+
// encode the message
33+
if(encoder_it->second->_encode(&encoder, message) == Encoder::Status::Error) {
34+
return Encoder::Status::Error;
35+
}
36+
37+
len = cbor_encoder_get_buffer_size(&encoder, data);
38+
39+
return Encoder::Status::Complete;
40+
}
41+
42+
CBORMessageEncoderSingleton& CBORMessageEncoderSingleton::getInstance() {
43+
static CBORMessageEncoderSingleton singleton;
44+
45+
return singleton;
46+
}
47+
48+
void CBORMessageEncoderSingleton::append(MessageId id, CBORMessageEncoderInterface* encoder) {
49+
auto encoder_it = encoders.begin();
50+
51+
for(; encoder_it != encoders.end(); encoder_it++) {
52+
if(encoder_it->first == id) {
53+
return;
54+
}
55+
}
56+
57+
encoders.push_back(
58+
std::make_pair(id, encoder)
59+
);
60+
}
61+
62+
CBORMessageEncoderInterface::CBORMessageEncoderInterface(const CBORTag tag, const MessageId id)
63+
: tag(tag), id(id) {
64+
// call singleton/global variable and insert this encoder
65+
CBORMessageEncoderSingleton::getInstance().append(id, this);
66+
}
67+
68+
Encoder::Status CBORMessageEncoderInterface::_encode(CborEncoder* encoder, Message *msg) {
69+
// this must always be true, it could mean that there are issues in the map of encoders
70+
assert(msg->id == id);
71+
72+
if (tag == cbor::tag::CBORUnknownCmdTag16b ||
73+
tag == cbor::tag::CBORUnknownCmdTag32b ||
74+
tag == cbor::tag::CBORUnknownCmdTag64b ||
75+
cbor_encode_tag(encoder, tag) != CborNoError) {
76+
return Encoder::Status::Error;
77+
}
78+
79+
return this->encode(encoder, msg);
80+
}

src/cbor/CborEncoder.h

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
This file is part of the Arduino_CloudUtils library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
#pragma once
11+
12+
#include <vector>
13+
#include "../interfaces/Encoder.h"
14+
#include "CBOR.h"
15+
#include "../interfaces/message.h"
16+
#include "./tinycbor/cbor-lib.h"
17+
18+
// forward declaration
19+
class CBORMessageEncoderSingleton;
20+
21+
/*
22+
* This library collects the interfaces used to encode Messages. Messages are C structs
23+
* that are identified by a uint32 id after which they can contain anything.
24+
* The objective of this library is to be modular and extensible in other libraries.
25+
*
26+
* In order to do so one have to extend the class `CBORMessageEncoderInterface`,
27+
* provide the associated CBORTag and MessageId and provide a way to encode the message
28+
* specific data. MessageId must be univocal across all EncoderInterfaces instantiated.
29+
* The class implemented must be instantiated somewhere (using extern may be helpful)
30+
* and that is enough to have it included in the encode process.
31+
*
32+
* In order to encode a message one can instantiate `CBORMessageEncoder` and
33+
* call the encode function.
34+
*/
35+
36+
/**
37+
* CBORMessageEncoderInterface class is an abstract class one has to implement
38+
* in order to provide the instructions needed to encode a buffer contasining
39+
* a message specific data.
40+
*/
41+
class CBORMessageEncoderInterface {
42+
public:
43+
44+
/**
45+
* Constructor that initializes the CBORMessageEncoderInterface by providing the associated
46+
* CBORTag and MessageId. The constructor also appends the object into CBORMessageEncoderSingleton
47+
* allowing it to be used in CBORMessageEncoderSingleton::encode
48+
*
49+
* @param tag the cbor tag the message is associated with
50+
* @param id the message id the message is associated with
51+
*/
52+
CBORMessageEncoderInterface(const CBORTag tag, const MessageId id);
53+
virtual ~CBORMessageEncoderInterface() {}
54+
55+
protected:
56+
57+
/**
58+
* Abstract encode function one must implement to encode a Message into a provided buffer
59+
*
60+
* @param encoder tinycbor encoder struct
61+
* @param msg The message from which data must be extracted
62+
*/
63+
virtual Encoder::Status encode(CborEncoder* encoder, Message *msg) = 0;
64+
65+
private:
66+
const CBORTag tag;
67+
const MessageId id;
68+
69+
friend CBORMessageEncoderSingleton;
70+
71+
/**
72+
* Encode wrapper function that encodes the cbor tag and then calls the encode virtual function
73+
*
74+
* @param encoder tinycbor encoder struct
75+
* @param msg The message from which data must be extracted
76+
*/
77+
Encoder::Status _encode(CborEncoder* encoder, Message *msg);
78+
};
79+
80+
/**
81+
* This class is a singleton. It collects CBORMessageEncoderInterfaces implementations to encode
82+
* all possible CBOR encoded messages
83+
*/
84+
class CBORMessageEncoderSingleton: public Encoder {
85+
public:
86+
/**
87+
* Get the singleton instance
88+
*/
89+
static CBORMessageEncoderSingleton& getInstance();
90+
91+
/**
92+
* Add a new encoder to the singleton instance associating it to the provided cbor tag
93+
*
94+
* @param tag the tag to which we associate the encoder
95+
* @param encoder the instance of encoder to append
96+
*/
97+
void append(MessageId id, CBORMessageEncoderInterface* encoder);
98+
99+
/**
100+
* Encode a message and put the contents into a byte buffer. The message should be big enough
101+
* to accommodate the content of the buffer.
102+
*
103+
* @param[in] msg A struct that embeds struct Message
104+
* @param[in out] buf The output buffer onto which the message will be encoded
105+
* @param[in out] len The length of the buffer
106+
*/
107+
Encoder::Status encode(Message* message, uint8_t * data, size_t& len);
108+
private:
109+
CBORMessageEncoderSingleton() {}
110+
111+
std::vector<std::pair<MessageId, CBORMessageEncoderInterface*>> encoders;
112+
};
113+
114+
/**
115+
* In order to encode a message one can instantiate `CBORMessageEncoder` and
116+
* call the encode function. In the future this class will contain encode session related
117+
* information, in order to allow streaming decoding
118+
*/
119+
class CBORMessageEncoder: public Encoder {
120+
public:
121+
/**
122+
* Encode a message and put the contents into a byte buffer. The message should be big enough
123+
* to accommodate the content of the buffer.
124+
*
125+
* @param[in] msg A struct that embeds struct Message
126+
* @param[in out] buf The output buffer onto which the message will be encoded
127+
* @param[in out] len The length of the buffer
128+
*/
129+
inline Encoder::Status encode(Message* msg, uint8_t* buf, size_t &len) {
130+
return CBORMessageEncoderSingleton::getInstance().encode(msg, buf, len);
131+
}
132+
};

0 commit comments

Comments
 (0)