blob: f6624c7f06c3fb0bcb9a0d52405035c3b20f4560 [file] [log] [blame]
kasunathb81dcec2022-05-23 12:23:44 -07001#include "bej_decoder_json.hpp"
2
3namespace libbej
4{
5
6/**
7 * @brief This structure is used to pass additional data to callback functions.
8 */
9struct BejJsonParam
10{
11 bool* isPrevAnnotated;
12 std::string* output;
13};
14
15/**
16 * @brief Add a property name to output buffer.
17 *
18 * @param[in] params - a valid BejJsonParam struct.
19 * @param[in] propertyName - a NULL terminated string.
20 */
21static void addPropertyNameToOutput(struct BejJsonParam* params,
22 const char* propertyName)
23{
24 if (propertyName[0] == '\0')
25 {
26 return;
27 }
28 if (!(*params->isPrevAnnotated))
29 {
30 params->output->push_back('\"');
31 }
32 params->output->append(propertyName);
33 params->output->append("\":");
34}
35
36/**
37 * @brief Callback for bejSet start.
38 *
39 * @param[in] propertyName - a NULL terminated string.
40 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
41 * @return 0 if successful.
42 */
43static int callbackSetStart(const char* propertyName, void* dataPtr)
44{
45 struct BejJsonParam* params =
46 reinterpret_cast<struct BejJsonParam*>(dataPtr);
47 addPropertyNameToOutput(params, propertyName);
48 params->output->push_back('{');
49 *params->isPrevAnnotated = false;
50 return 0;
51}
52
53/**
54 * @brief Callback for bejSet end.
55 *
56 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
57 * @return 0 if successful.
58 */
59static int callbackSetEnd(void* dataPtr)
60{
61 struct BejJsonParam* params =
62 reinterpret_cast<struct BejJsonParam*>(dataPtr);
63 params->output->push_back('}');
64 return 0;
65}
66
67/**
68 * @brief Callback for bejArray start.
69 *
70 * @param[in] propertyName - a NULL terminated string.
71 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
72 * @return 0 if successful.
73 */
74static int callbackArrayStart(const char* propertyName, void* dataPtr)
75{
76 struct BejJsonParam* params =
77 reinterpret_cast<struct BejJsonParam*>(dataPtr);
78 addPropertyNameToOutput(params, propertyName);
79 params->output->push_back('[');
80 *params->isPrevAnnotated = false;
81 return 0;
82}
83
84/**
85 * @brief Callback for bejArray end.
86 *
87 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
88 * @return 0 if successful.
89 */
90static int callbackArrayEnd(void* dataPtr)
91{
92 struct BejJsonParam* params =
93 reinterpret_cast<struct BejJsonParam*>(dataPtr);
94 params->output->push_back(']');
95 return 0;
96}
97
98/**
99 * @brief Callback when an end of a property is detected.
100 *
101 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
102 * @return 0 if successful.
103 */
104static int callbackPropertyEnd(void* dataPtr)
105{
106 struct BejJsonParam* params =
107 reinterpret_cast<struct BejJsonParam*>(dataPtr);
108 // Not a section ending. So add a comma.
109 params->output->push_back(',');
110 return 0;
111}
112
113/**
114 * @brief Callback for bejNull type.
115 *
116 * @param[in] propertyName - a NULL terminated string.
117 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
118 * @return 0 if successful.
119 */
120static int callbackNull(const char* propertyName, void* dataPtr)
121{
122 struct BejJsonParam* params =
123 reinterpret_cast<struct BejJsonParam*>(dataPtr);
124 addPropertyNameToOutput(params, propertyName);
125 params->output->append("null");
126 *params->isPrevAnnotated = false;
127 return 0;
128}
129
130/**
131 * @brief Callback for bejInteger type.
132 *
133 * @param[in] propertyName - a NULL terminated string.
134 * @param[in] value - integer value.
135 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
136 * @return 0 if successful.
137 */
138static int callbackInteger(const char* propertyName, int64_t value,
139 void* dataPtr)
140{
141 struct BejJsonParam* params =
142 reinterpret_cast<struct BejJsonParam*>(dataPtr);
143 addPropertyNameToOutput(params, propertyName);
144 params->output->append(std::to_string(value));
145 *params->isPrevAnnotated = false;
146 return 0;
147}
148
149/**
150 * @brief Callback for bejEnum type.
151 *
152 * @param[in] propertyName - a NULL terminated string.
153 * @param[in] value - a NULL terminated string.
154 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
155 * @return 0 if successful.
156 */
157static int callbackEnum(const char* propertyName, const char* value,
158 void* dataPtr)
159{
160 struct BejJsonParam* params =
161 reinterpret_cast<struct BejJsonParam*>(dataPtr);
162 addPropertyNameToOutput(params, propertyName);
163 params->output->push_back('\"');
164 params->output->append(value);
165 params->output->push_back('\"');
166 *params->isPrevAnnotated = false;
167 return 0;
168}
169
170/**
171 * @brief Callback for bejString type.
172 *
173 * @param[in] propertyName - a NULL terminated string.
174 * @param[in] value - a NULL terminated string.
175 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
176 * @return 0 if successful.
177 */
178static int callbackString(const char* propertyName, const char* value,
179 void* dataPtr)
180{
181 struct BejJsonParam* params =
182 reinterpret_cast<struct BejJsonParam*>(dataPtr);
183 addPropertyNameToOutput(params, propertyName);
184 params->output->push_back('\"');
185 params->output->append(value);
186 params->output->push_back('\"');
187 *params->isPrevAnnotated = false;
188 return 0;
189}
190
191/**
192 * @brief Callback for bejReal type.
193 *
194 * @param[in] propertyName - a NULL terminated string.
195 * @param[in] value - pointing to a valid BejReal.
196 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
197 * @return 0 if successful.
198 */
199static int callbackReal(const char* propertyName, const struct BejReal* value,
200 void* dataPtr)
201{
202 struct BejJsonParam* params =
203 reinterpret_cast<struct BejJsonParam*>(dataPtr);
204
205 addPropertyNameToOutput(params, propertyName);
206 params->output->append(std::to_string(value->whole));
207 params->output->push_back('.');
208 params->output->insert(params->output->cend(), value->zeroCount, '0');
209 params->output->append(std::to_string(value->fract));
210 if (value->expLen != 0)
211 {
212 params->output->push_back('e');
213 params->output->append(std::to_string(value->exp));
214 }
215 *params->isPrevAnnotated = false;
216 return 0;
217}
218
219/**
220 * @brief Callback for bejBoolean type.
221 *
222 * @param[in] propertyName - a NULL terminated string.
223 * @param[in] value - boolean value.
224 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
225 * @return 0 if successful.
226 */
227static int callbackBool(const char* propertyName, bool value, void* dataPtr)
228{
229 struct BejJsonParam* params =
230 reinterpret_cast<struct BejJsonParam*>(dataPtr);
231 addPropertyNameToOutput(params, propertyName);
232 params->output->append(value ? "true" : "false");
233 *params->isPrevAnnotated = false;
234 return 0;
235}
236
237/**
238 * @brief Callback for bejPropertyAnnotation type.
239 *
240 * @param[in] propertyName - a NULL terminated string.
241 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
242 * @return 0 if successful.
243 */
244static int callbackAnnotation(const char* propertyName, void* dataPtr)
245{
246 struct BejJsonParam* params =
247 reinterpret_cast<struct BejJsonParam*>(dataPtr);
248 params->output->push_back('\"');
249 params->output->append(propertyName);
250
251 // bejPropertyAnnotation type has the form "Status@Message.ExtendedInfo".
252 // First the decoder will see "Status" part of the annotated property. This
253 // will be in its own SFLV tuple. The remainder of the property name,
254 // @Message.ExtendedInfo will be contained in the next bej SFLV tuple.
255 // Therefore to add the inverted commas to the complete property name,
256 // Status@Message.ExtendedInfo, we need to know that the previous property
257 // we processed is a start to an annotation property. We can use
258 // isPrevAnnotated to pass this information.
259 // Here we are adding: "propertyName
260 // If isPrevAnnotated is true, next property should add: propertyNameNext"
261 *params->isPrevAnnotated = true;
262 return 0;
263}
264
265/**
266 * @brief Callback for stackEmpty.
267 *
268 * @param[in] dataPtr - pointer to a valid std::vector<BejStackProperty>
269 * @return true if the stack is empty.
270 */
271static bool stackEmpty(void* dataPtr)
272{
273 std::vector<BejStackProperty>* stack =
274 reinterpret_cast<std::vector<BejStackProperty>*>(dataPtr);
275 return stack->empty();
276}
277
278/**
279 * @brief Callback for stackPeek.
280 *
281 * @param[in] dataPtr - pointer to a valid std::vector<BejStackProperty>
282 * @return a const reference to the stack top.
283 */
284static const struct BejStackProperty* stackPeek(void* dataPtr)
285{
286 std::vector<BejStackProperty>* stack =
287 reinterpret_cast<std::vector<BejStackProperty>*>(dataPtr);
288 if (stack->empty())
289 {
290 return nullptr;
291 }
292 return &(stack->back());
293}
294
295/**
296 * @brief Callback for stackPop. Remove the top element from the stack.
297 *
298 * @param[in] dataPtr - pointer to a valid std::vector<BejStackProperty>
299 */
300static void stackPop(void* dataPtr)
301{
302 std::vector<BejStackProperty>* stack =
303 reinterpret_cast<std::vector<BejStackProperty>*>(dataPtr);
304 if (stack->empty())
305 {
306 return;
307 }
308 stack->pop_back();
309}
310
311/**
312 * @brief Callback for stackPush. Push a new element to the top of the stack.
313 *
314 * @param[in] property - property to push.
315 * @param[in] dataPtr - pointer to a valid std::vector<BejStackProperty>
316 * @return 0 if successful.
317 */
318static int stackPush(const struct BejStackProperty* const property,
319 void* dataPtr)
320{
321 std::vector<BejStackProperty>* stack =
322 reinterpret_cast<std::vector<BejStackProperty>*>(dataPtr);
323 stack->push_back(*property);
324 return 0;
325}
326
327int BejDecoderJson::decode(const BejDictionaries& dictionaries,
328 const std::span<const uint8_t> encodedPldmBlock)
329{
330 // Clear the previous output if any.
331 output.clear();
332
333 // The dictionaries have to be traversed in a depth first manner. This is
334 // using a stack to implement it non-recursively. Going into a set or an
335 // array or a property annotation section means that we have to jump to the
336 // child dictionary offset start point but needs to retrieve the parent
337 // dictionary offset start once all the children are processed. This stack
338 // will hold the parent dictionary offsets and endings for each section.
339 stack.clear();
340
341 struct BejStackCallback stackCallback = {
342 .stackEmpty = stackEmpty,
343 .stackPeek = stackPeek,
344 .stackPop = stackPop,
345 .stackPush = stackPush,
346 };
347
348 struct BejDecodedCallback decodedCallback = {
349 .callbackSetStart = callbackSetStart,
350 .callbackSetEnd = callbackSetEnd,
351 .callbackArrayStart = callbackArrayStart,
352 .callbackArrayEnd = callbackArrayEnd,
353 .callbackPropertyEnd = callbackPropertyEnd,
354 .callbackNull = callbackNull,
355 .callbackInteger = callbackInteger,
356 .callbackEnum = callbackEnum,
357 .callbackString = callbackString,
358 .callbackReal = callbackReal,
359 .callbackBool = callbackBool,
360 .callbackAnnotation = callbackAnnotation,
361 .callbackReadonlyProperty = nullptr,
362 };
363
364 isPrevAnnotated = false;
365 struct BejJsonParam callbackData = {
366 .isPrevAnnotated = &isPrevAnnotated,
367 .output = &output,
368 };
369
370 return bejDecodePldmBlock(&dictionaries, encodedPldmBlock.data(),
371 encodedPldmBlock.size_bytes(), &stackCallback,
372 &decodedCallback, (void*)(&callbackData),
373 (void*)(&stack));
374}
375
376std::string BejDecoderJson::getOutput()
377{
378 return output;
379}
380
381} // namespace libbej