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