xref: /openbmc/libbej/src/bej_decoder_json.cpp (revision 9eb0117f0f5e5ed8ffe82d51f70d7af3b9baf6a0)
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