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