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