1 #include "bej_decoder_json.hpp"
2
3 namespace libbej
4 {
5
6 /**
7 * @brief This structure is used to pass additional data to callback functions.
8 */
9 struct 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 */
addPropertyNameToOutput(struct BejJsonParam * params,const char * propertyName)21 static 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 */
callbackSetStart(const char * propertyName,void * dataPtr)43 static 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 */
callbackSetEnd(void * dataPtr)59 static 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 */
callbackArrayStart(const char * propertyName,void * dataPtr)74 static 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 */
callbackArrayEnd(void * dataPtr)90 static 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 */
callbackPropertyEnd(void * dataPtr)104 static 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 */
callbackNull(const char * propertyName,void * dataPtr)120 static 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 */
callbackInteger(const char * propertyName,int64_t value,void * dataPtr)138 static 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 */
callbackEnum(const char * propertyName,const char * value,void * dataPtr)157 static 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 */
callbackString(const char * propertyName,const char * value,void * dataPtr)178 static 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 */
callbackReal(const char * propertyName,const struct BejReal * value,void * dataPtr)199 static 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 // Sanity check for zeroCount
206 if (value->zeroCount > 100)
207 {
208 return bejErrorInvalidSize;
209 }
210
211 addPropertyNameToOutput(params, propertyName);
212 params->output->append(std::to_string(value->whole));
213 params->output->push_back('.');
214 params->output->insert(params->output->cend(), value->zeroCount, '0');
215 params->output->append(std::to_string(value->fract));
216 if (value->expLen != 0)
217 {
218 params->output->push_back('e');
219 params->output->append(std::to_string(value->exp));
220 }
221 *params->isPrevAnnotated = false;
222 return 0;
223 }
224
225 /**
226 * @brief Callback for bejBoolean type.
227 *
228 * @param[in] propertyName - a NULL terminated string.
229 * @param[in] value - boolean value.
230 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
231 * @return 0 if successful.
232 */
callbackBool(const char * propertyName,bool value,void * dataPtr)233 static int callbackBool(const char* propertyName, bool value, void* dataPtr)
234 {
235 struct BejJsonParam* params =
236 reinterpret_cast<struct BejJsonParam*>(dataPtr);
237 addPropertyNameToOutput(params, propertyName);
238 params->output->append(value ? "true" : "false");
239 *params->isPrevAnnotated = false;
240 return 0;
241 }
242
243 /**
244 * @brief Callback for bejPropertyAnnotation type.
245 *
246 * @param[in] propertyName - a NULL terminated string.
247 * @param[in] dataPtr - pointing to a valid BejJsonParam struct.
248 * @return 0 if successful.
249 */
callbackAnnotation(const char * propertyName,void * dataPtr)250 static int callbackAnnotation(const char* propertyName, void* dataPtr)
251 {
252 struct BejJsonParam* params =
253 reinterpret_cast<struct BejJsonParam*>(dataPtr);
254 params->output->push_back('\"');
255 params->output->append(propertyName);
256
257 // bejPropertyAnnotation type has the form "Status@Message.ExtendedInfo".
258 // First the decoder will see "Status" part of the annotated property. This
259 // will be in its own SFLV tuple. The remainder of the property name,
260 // @Message.ExtendedInfo will be contained in the next bej SFLV tuple.
261 // Therefore to add the inverted commas to the complete property name,
262 // Status@Message.ExtendedInfo, we need to know that the previous property
263 // we processed is a start to an annotation property. We can use
264 // isPrevAnnotated to pass this information.
265 // Here we are adding: "propertyName
266 // If isPrevAnnotated is true, next property should add: propertyNameNext"
267 *params->isPrevAnnotated = true;
268 return 0;
269 }
270
271 /**
272 * @brief Callback for stackEmpty.
273 *
274 * @param[in] dataPtr - pointer to a valid std::vector<BejStackProperty>
275 * @return true if the stack is empty.
276 */
stackEmpty(void * dataPtr)277 static bool stackEmpty(void* dataPtr)
278 {
279 std::vector<BejStackProperty>* stack =
280 reinterpret_cast<std::vector<BejStackProperty>*>(dataPtr);
281 return stack->empty();
282 }
283
284 /**
285 * @brief Callback for stackPeek.
286 *
287 * @param[in] dataPtr - pointer to a valid std::vector<BejStackProperty>
288 * @return a const reference to the stack top.
289 */
stackPeek(void * dataPtr)290 static const struct BejStackProperty* stackPeek(void* dataPtr)
291 {
292 std::vector<BejStackProperty>* stack =
293 reinterpret_cast<std::vector<BejStackProperty>*>(dataPtr);
294 if (stack->empty())
295 {
296 return nullptr;
297 }
298 return &(stack->back());
299 }
300
301 /**
302 * @brief Callback for stackPop. Remove the top element from the stack.
303 *
304 * @param[in] dataPtr - pointer to a valid std::vector<BejStackProperty>
305 */
stackPop(void * dataPtr)306 static void stackPop(void* dataPtr)
307 {
308 std::vector<BejStackProperty>* stack =
309 reinterpret_cast<std::vector<BejStackProperty>*>(dataPtr);
310 if (stack->empty())
311 {
312 return;
313 }
314 stack->pop_back();
315 }
316
317 /**
318 * @brief Callback for stackPush. Push a new element to the top of the stack.
319 *
320 * @param[in] property - property to push.
321 * @param[in] dataPtr - pointer to a valid std::vector<BejStackProperty>
322 * @return 0 if successful.
323 */
stackPush(const struct BejStackProperty * const property,void * dataPtr)324 static int stackPush(const struct BejStackProperty* const property,
325 void* dataPtr)
326 {
327 std::vector<BejStackProperty>* stack =
328 reinterpret_cast<std::vector<BejStackProperty>*>(dataPtr);
329 stack->push_back(*property);
330 return 0;
331 }
332
decode(const BejDictionaries & dictionaries,const std::span<const uint8_t> encodedPldmBlock)333 int BejDecoderJson::decode(const BejDictionaries& dictionaries,
334 const std::span<const uint8_t> encodedPldmBlock)
335 {
336 // Clear the previous output if any.
337 output.clear();
338
339 // The dictionaries have to be traversed in a depth first manner. This is
340 // using a stack to implement it non-recursively. Going into a set or an
341 // array or a property annotation section means that we have to jump to the
342 // child dictionary offset start point but needs to retrieve the parent
343 // dictionary offset start once all the children are processed. This stack
344 // will hold the parent dictionary offsets and endings for each section.
345 stack.clear();
346
347 struct BejStackCallback stackCallback = {
348 .stackEmpty = stackEmpty,
349 .stackPeek = stackPeek,
350 .stackPop = stackPop,
351 .stackPush = stackPush,
352 };
353
354 struct BejDecodedCallback decodedCallback = {
355 .callbackSetStart = callbackSetStart,
356 .callbackSetEnd = callbackSetEnd,
357 .callbackArrayStart = callbackArrayStart,
358 .callbackArrayEnd = callbackArrayEnd,
359 .callbackPropertyEnd = callbackPropertyEnd,
360 .callbackNull = callbackNull,
361 .callbackInteger = callbackInteger,
362 .callbackEnum = callbackEnum,
363 .callbackString = callbackString,
364 .callbackReal = callbackReal,
365 .callbackBool = callbackBool,
366 .callbackAnnotation = callbackAnnotation,
367 .callbackReadonlyProperty = nullptr,
368 };
369
370 isPrevAnnotated = false;
371 struct BejJsonParam callbackData = {
372 .isPrevAnnotated = &isPrevAnnotated,
373 .output = &output,
374 };
375
376 return bejDecodePldmBlock(
377 &dictionaries, encodedPldmBlock.data(), encodedPldmBlock.size_bytes(),
378 &stackCallback, &decodedCallback, (void*)(&callbackData),
379 (void*)(&stack));
380 }
381
getOutput()382 std::string BejDecoderJson::getOutput()
383 {
384 return output;
385 }
386
387 } // namespace libbej
388