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