Skip to content

Commit a7716ad

Browse files
implementing encoder interface
1 parent 6dd1bd7 commit a7716ad

File tree

2 files changed

+209
-0
lines changed

2 files changed

+209
-0
lines changed

src/cbor/CborEncoder.cpp

Lines changed: 80 additions & 0 deletions
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

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

0 commit comments

Comments
 (0)