blob: f6624c7f06c3fb0bcb9a0d52405035c3b20f4560 [file] [log] [blame]
#include "bej_decoder_json.hpp"
namespace libbej
{
/**
* @brief This structure is used to pass additional data to callback functions.
*/
struct BejJsonParam
{
bool* isPrevAnnotated;
std::string* output;
};
/**
* @brief Add a property name to output buffer.
*
* @param[in] params - a valid BejJsonParam struct.
* @param[in] propertyName - a NULL terminated string.
*/
static void addPropertyNameToOutput(struct BejJsonParam* params,
const char* propertyName)
{
if (propertyName[0] == '\0')
{
return;
}
if (!(*params->isPrevAnnotated))
{
params->output->push_back('\"');
}
params->output->append(propertyName);
params->output->append("\":");
}
/**
* @brief Callback for bejSet start.
*
* @param[in] propertyName - a NULL terminated string.
* @param[in] dataPtr - pointing to a valid BejJsonParam struct.
* @return 0 if successful.
*/
static int callbackSetStart(const char* propertyName, void* dataPtr)
{
struct BejJsonParam* params =
reinterpret_cast<struct BejJsonParam*>(dataPtr);
addPropertyNameToOutput(params, propertyName);
params->output->push_back('{');
*params->isPrevAnnotated = false;
return 0;
}
/**
* @brief Callback for bejSet end.
*
* @param[in] dataPtr - pointing to a valid BejJsonParam struct.
* @return 0 if successful.
*/
static int callbackSetEnd(void* dataPtr)
{
struct BejJsonParam* params =
reinterpret_cast<struct BejJsonParam*>(dataPtr);
params->output->push_back('}');
return 0;
}
/**
* @brief Callback for bejArray start.
*
* @param[in] propertyName - a NULL terminated string.
* @param[in] dataPtr - pointing to a valid BejJsonParam struct.
* @return 0 if successful.
*/
static int callbackArrayStart(const char* propertyName, void* dataPtr)
{
struct BejJsonParam* params =
reinterpret_cast<struct BejJsonParam*>(dataPtr);
addPropertyNameToOutput(params, propertyName);
params->output->push_back('[');
*params->isPrevAnnotated = false;
return 0;
}
/**
* @brief Callback for bejArray end.
*
* @param[in] dataPtr - pointing to a valid BejJsonParam struct.
* @return 0 if successful.
*/
static int callbackArrayEnd(void* dataPtr)
{
struct BejJsonParam* params =
reinterpret_cast<struct BejJsonParam*>(dataPtr);
params->output->push_back(']');
return 0;
}
/**
* @brief Callback when an end of a property is detected.
*
* @param[in] dataPtr - pointing to a valid BejJsonParam struct.
* @return 0 if successful.
*/
static int callbackPropertyEnd(void* dataPtr)
{
struct BejJsonParam* params =
reinterpret_cast<struct BejJsonParam*>(dataPtr);
// Not a section ending. So add a comma.
params->output->push_back(',');
return 0;
}
/**
* @brief Callback for bejNull type.
*
* @param[in] propertyName - a NULL terminated string.
* @param[in] dataPtr - pointing to a valid BejJsonParam struct.
* @return 0 if successful.
*/
static int callbackNull(const char* propertyName, void* dataPtr)
{
struct BejJsonParam* params =
reinterpret_cast<struct BejJsonParam*>(dataPtr);
addPropertyNameToOutput(params, propertyName);
params->output->append("null");
*params->isPrevAnnotated = false;
return 0;
}
/**
* @brief Callback for bejInteger type.
*
* @param[in] propertyName - a NULL terminated string.
* @param[in] value - integer value.
* @param[in] dataPtr - pointing to a valid BejJsonParam struct.
* @return 0 if successful.
*/
static int callbackInteger(const char* propertyName, int64_t value,
void* dataPtr)
{
struct BejJsonParam* params =
reinterpret_cast<struct BejJsonParam*>(dataPtr);
addPropertyNameToOutput(params, propertyName);
params->output->append(std::to_string(value));
*params->isPrevAnnotated = false;
return 0;
}
/**
* @brief Callback for bejEnum type.
*
* @param[in] propertyName - a NULL terminated string.
* @param[in] value - a NULL terminated string.
* @param[in] dataPtr - pointing to a valid BejJsonParam struct.
* @return 0 if successful.
*/
static int callbackEnum(const char* propertyName, const char* value,
void* dataPtr)
{
struct BejJsonParam* params =
reinterpret_cast<struct BejJsonParam*>(dataPtr);
addPropertyNameToOutput(params, propertyName);
params->output->push_back('\"');
params->output->append(value);
params->output->push_back('\"');
*params->isPrevAnnotated = false;
return 0;
}
/**
* @brief Callback for bejString type.
*
* @param[in] propertyName - a NULL terminated string.
* @param[in] value - a NULL terminated string.
* @param[in] dataPtr - pointing to a valid BejJsonParam struct.
* @return 0 if successful.
*/
static int callbackString(const char* propertyName, const char* value,
void* dataPtr)
{
struct BejJsonParam* params =
reinterpret_cast<struct BejJsonParam*>(dataPtr);
addPropertyNameToOutput(params, propertyName);
params->output->push_back('\"');
params->output->append(value);
params->output->push_back('\"');
*params->isPrevAnnotated = false;
return 0;
}
/**
* @brief Callback for bejReal type.
*
* @param[in] propertyName - a NULL terminated string.
* @param[in] value - pointing to a valid BejReal.
* @param[in] dataPtr - pointing to a valid BejJsonParam struct.
* @return 0 if successful.
*/
static int callbackReal(const char* propertyName, const struct BejReal* value,
void* dataPtr)
{
struct BejJsonParam* params =
reinterpret_cast<struct BejJsonParam*>(dataPtr);
addPropertyNameToOutput(params, propertyName);
params->output->append(std::to_string(value->whole));
params->output->push_back('.');
params->output->insert(params->output->cend(), value->zeroCount, '0');
params->output->append(std::to_string(value->fract));
if (value->expLen != 0)
{
params->output->push_back('e');
params->output->append(std::to_string(value->exp));
}
*params->isPrevAnnotated = false;
return 0;
}
/**
* @brief Callback for bejBoolean type.
*
* @param[in] propertyName - a NULL terminated string.
* @param[in] value - boolean value.
* @param[in] dataPtr - pointing to a valid BejJsonParam struct.
* @return 0 if successful.
*/
static int callbackBool(const char* propertyName, bool value, void* dataPtr)
{
struct BejJsonParam* params =
reinterpret_cast<struct BejJsonParam*>(dataPtr);
addPropertyNameToOutput(params, propertyName);
params->output->append(value ? "true" : "false");
*params->isPrevAnnotated = false;
return 0;
}
/**
* @brief Callback for bejPropertyAnnotation type.
*
* @param[in] propertyName - a NULL terminated string.
* @param[in] dataPtr - pointing to a valid BejJsonParam struct.
* @return 0 if successful.
*/
static int callbackAnnotation(const char* propertyName, void* dataPtr)
{
struct BejJsonParam* params =
reinterpret_cast<struct BejJsonParam*>(dataPtr);
params->output->push_back('\"');
params->output->append(propertyName);
// bejPropertyAnnotation type has the form "Status@Message.ExtendedInfo".
// First the decoder will see "Status" part of the annotated property. This
// will be in its own SFLV tuple. The remainder of the property name,
// @Message.ExtendedInfo will be contained in the next bej SFLV tuple.
// Therefore to add the inverted commas to the complete property name,
// Status@Message.ExtendedInfo, we need to know that the previous property
// we processed is a start to an annotation property. We can use
// isPrevAnnotated to pass this information.
// Here we are adding: "propertyName
// If isPrevAnnotated is true, next property should add: propertyNameNext"
*params->isPrevAnnotated = true;
return 0;
}
/**
* @brief Callback for stackEmpty.
*
* @param[in] dataPtr - pointer to a valid std::vector<BejStackProperty>
* @return true if the stack is empty.
*/
static bool stackEmpty(void* dataPtr)
{
std::vector<BejStackProperty>* stack =
reinterpret_cast<std::vector<BejStackProperty>*>(dataPtr);
return stack->empty();
}
/**
* @brief Callback for stackPeek.
*
* @param[in] dataPtr - pointer to a valid std::vector<BejStackProperty>
* @return a const reference to the stack top.
*/
static const struct BejStackProperty* stackPeek(void* dataPtr)
{
std::vector<BejStackProperty>* stack =
reinterpret_cast<std::vector<BejStackProperty>*>(dataPtr);
if (stack->empty())
{
return nullptr;
}
return &(stack->back());
}
/**
* @brief Callback for stackPop. Remove the top element from the stack.
*
* @param[in] dataPtr - pointer to a valid std::vector<BejStackProperty>
*/
static void stackPop(void* dataPtr)
{
std::vector<BejStackProperty>* stack =
reinterpret_cast<std::vector<BejStackProperty>*>(dataPtr);
if (stack->empty())
{
return;
}
stack->pop_back();
}
/**
* @brief Callback for stackPush. Push a new element to the top of the stack.
*
* @param[in] property - property to push.
* @param[in] dataPtr - pointer to a valid std::vector<BejStackProperty>
* @return 0 if successful.
*/
static int stackPush(const struct BejStackProperty* const property,
void* dataPtr)
{
std::vector<BejStackProperty>* stack =
reinterpret_cast<std::vector<BejStackProperty>*>(dataPtr);
stack->push_back(*property);
return 0;
}
int BejDecoderJson::decode(const BejDictionaries& dictionaries,
const std::span<const uint8_t> encodedPldmBlock)
{
// Clear the previous output if any.
output.clear();
// The dictionaries have to be traversed in a depth first manner. This is
// using a stack to implement it non-recursively. Going into a set or an
// array or a property annotation section means that we have to jump to the
// child dictionary offset start point but needs to retrieve the parent
// dictionary offset start once all the children are processed. This stack
// will hold the parent dictionary offsets and endings for each section.
stack.clear();
struct BejStackCallback stackCallback = {
.stackEmpty = stackEmpty,
.stackPeek = stackPeek,
.stackPop = stackPop,
.stackPush = stackPush,
};
struct BejDecodedCallback decodedCallback = {
.callbackSetStart = callbackSetStart,
.callbackSetEnd = callbackSetEnd,
.callbackArrayStart = callbackArrayStart,
.callbackArrayEnd = callbackArrayEnd,
.callbackPropertyEnd = callbackPropertyEnd,
.callbackNull = callbackNull,
.callbackInteger = callbackInteger,
.callbackEnum = callbackEnum,
.callbackString = callbackString,
.callbackReal = callbackReal,
.callbackBool = callbackBool,
.callbackAnnotation = callbackAnnotation,
.callbackReadonlyProperty = nullptr,
};
isPrevAnnotated = false;
struct BejJsonParam callbackData = {
.isPrevAnnotated = &isPrevAnnotated,
.output = &output,
};
return bejDecodePldmBlock(&dictionaries, encodedPldmBlock.data(),
encodedPldmBlock.size_bytes(), &stackCallback,
&decodedCallback, (void*)(&callbackData),
(void*)(&stack));
}
std::string BejDecoderJson::getOutput()
{
return output;
}
} // namespace libbej