blob: e8fba00e40083739aa22014777c370b265eb89c5 [file] [log] [blame]
#include "bej_decoder_core.h"
#include "bej_dictionary.h"
#include "stdio.h"
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
// TODO: Support nested annotations for version 0xF1F1F000
const uint32_t supportedBejVersions[] = {0xF1F0F000};
/**
* @brief Call a callback function. If the callback function is NULL, this will
* not do anything. If the callback function returns a non-zero value, this will
* cause the caller to return with the non-zero status.
*/
#define RETURN_IF_CALLBACK_IERROR(function, ...) \
do \
{ \
if ((function) != NULL) \
{ \
int __status = ((function)(__VA_ARGS__)); \
if (__status != 0) \
{ \
return __status; \
} \
} \
} while (0)
/**
* @brief Check a given varable is NULL. If it is NULL, this will return with
* bejErrorNullParameter. If the variable is not NULL, this will will not
* return.
*/
#define NULL_CHECK(param, structStr) \
do \
{ \
if ((param) == NULL) \
{ \
fprintf(stderr, "nullCheck: %s cannot be null\n", structStr); \
return bejErrorNullParameter; \
} \
} while (0)
/**
* @brief Get the integer value from BEJ byte stream.
*
* @param[in] bytes - valid pointer to a byte stream in little-endian format.
* @param[in] numOfBytes - number of bytes belongs to the value. Maximum value
* supported is 8 bytes.
* @return signed 64bit representation of the value.
*/
static int64_t bejGetIntegerValue(const uint8_t* bytes, uint8_t numOfBytes)
{
if (numOfBytes == 0)
{
return 0;
}
uint64_t value = bejGetUnsignedInteger(bytes, numOfBytes);
uint8_t bitsInVal = numOfBytes * 8;
// Since numOfBytes > 0, bitsInVal is non negative.
uint64_t mask = (uint64_t)1 << (uint8_t)(bitsInVal - 1);
return (int64_t)((value ^ mask) - mask);
}
/**
* @brief Get offsets of SFLV fields with respect to the enSegment start.
*
* @param[in] enSegment - a valid pointer to a start of a SFLV bejTuple.
* @param[out] offsets - this will hold the local offsets.
*/
static void bejGetLocalBejSFLVOffsets(const uint8_t* enSegment,
struct BejSFLVOffset* offsets)
{
// Structure of the SFLV.
// [Number of bytes need to represent the sequence number] - uint8_t
// [SequenceNumber] - multi byte
// [Format] - uint8_t
// [Number of bytes need to represent the value length] - uint8_t
// [Value length] - multi byte
// Number of bytes need to represent the sequence number.
const uint8_t seqSize = *enSegment;
// Start of format.
const uint32_t formatOffset = sizeof(uint8_t) + seqSize;
// Start of length of the value-length bytes.
const uint32_t valueLenNnintOffset = formatOffset + sizeof(uint8_t);
// Number of bytes need to represent the value length.
const uint8_t valueLengthSize = *(enSegment + valueLenNnintOffset);
// Start of the Value.
const uint32_t valueOffset =
valueLenNnintOffset + sizeof(uint8_t) + valueLengthSize;
offsets->formatOffset = formatOffset;
offsets->valueLenNnintOffset = valueLenNnintOffset;
offsets->valueOffset = valueOffset;
}
/**
* @brief Initialize sflv struct in params struct.
*
* @param[inout] params - a valid BejHandleTypeFuncParam struct with
* params->state.encodedSubStream pointing to the start of the encoded stream
* and params->state.encodedStreamOffset pointing to the current bejTuple.
*/
static void bejInitSFLVStruct(struct BejHandleTypeFuncParam* params)
{
struct BejSFLVOffset localOffset;
// Get offsets of different SFLV fields with respect to start of the encoded
// segment.
bejGetLocalBejSFLVOffsets(params->state.encodedSubStream, &localOffset);
struct BejSFLV* sflv = &params->sflv;
const uint32_t valueLength = (uint32_t)(bejGetNnint(
params->state.encodedSubStream + localOffset.valueLenNnintOffset));
// Sequence number itself should be 16bits. Using 32bits for
// [sequence_number + schema_type].
uint32_t tupleS = (uint32_t)(bejGetNnint(params->state.encodedSubStream));
sflv->tupleS.schema = (uint8_t)(tupleS & DICTIONARY_TYPE_MASK);
sflv->tupleS.sequenceNumber =
(uint16_t)((tupleS & (~DICTIONARY_TYPE_MASK)) >>
DICTIONARY_SEQ_NUM_SHIFT);
sflv->format = *(struct BejTupleF*)(params->state.encodedSubStream +
localOffset.formatOffset);
sflv->valueLength = valueLength;
sflv->valueEndOffset = params->state.encodedStreamOffset +
localOffset.valueOffset + valueLength;
sflv->value = params->state.encodedSubStream + localOffset.valueOffset;
}
/**
* @brief Get the offset to the first tuple of a bejArray or bejSet.
*
* The first part of the value of a bejArray or a bejSet contains an nnint
* providing the number of elements/tuples. Offset is with respect to the start
* of the encoded stream.
*
* @param[in] params - a valid BejHandleTypeFuncParam struct.
* @return offset with respect to the start of the encoded stream.
*/
static uint32_t
bejGetFirstTupleOffset(const struct BejHandleTypeFuncParam* params)
{
struct BejSFLVOffset localOffset;
// Get the offset of the value with respect to the current encoded segment
// being decoded.
bejGetLocalBejSFLVOffsets(params->state.encodedSubStream, &localOffset);
return params->state.encodedStreamOffset + localOffset.valueOffset +
bejGetNnintSize(params->sflv.value);
}
/**
* @brief Get the correct property and the dictionary it belongs to.
*
* @param[in] params - a BejHandleTypeFuncParam struct pointing to valid
* dictionaries.
* @param[in] schemaType - indicate whether to use the annotation dictionary or
* the main schema dictionary.
* @param[in] sequenceNumber - sequence number to use for property search. Not
* using the params->sflv.tupleS.sequenceNumber from the provided params struct.
* @param[out] dictionary - if the function is successful, this will point to a
* valid dictionary to be used.
* @param[out] prop - if the function is successful, this will point to a valid
* property in a dictionary.
* @return 0 if successful.
*/
static int
bejGetDictionaryAndProperty(const struct BejHandleTypeFuncParam* params,
uint8_t schemaType, uint32_t sequenceNumber,
const uint8_t** dictionary,
const struct BejDictionaryProperty** prop)
{
uint16_t dictPropOffset;
// We need to pick the correct dictionary.
if (schemaType == bejPrimary)
{
*dictionary = params->mainDictionary;
dictPropOffset = params->state.mainDictPropOffset;
}
else if (schemaType == bejAnnotation)
{
*dictionary = params->annotDictionary;
dictPropOffset = params->state.annoDictPropOffset;
}
else
{
fprintf(stderr, "Failed to select a dictionary. schema type: %u\n",
schemaType);
return bejErrorInvalidSchemaType;
}
int ret =
bejDictGetProperty(*dictionary, dictPropOffset, sequenceNumber, prop);
if (ret != 0)
{
fprintf(stderr, "Failed to get dictionary property for offset: %u\n",
dictPropOffset);
return ret;
}
return 0;
}
/**
* @brief Find and return the property name of the current encoded segment. If
* the params->state.addPropertyName is false, this will return an empty string.
*
* @param[in] params - a valid populated BejHandleTypeFuncParam.
* @return 0 if successful.
*/
static const char* bejGetPropName(struct BejHandleTypeFuncParam* params)
{
const uint8_t* dictionary;
const struct BejDictionaryProperty* prop;
if (!params->state.addPropertyName ||
(bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
params->sflv.tupleS.sequenceNumber,
&dictionary, &prop) != 0))
{
return "";
}
return bejDictGetPropertyName(dictionary, prop->nameOffset,
prop->nameLength);
}
/**
* @brief Look for section endings.
*
* This figures out whether the current encoded segment marks a section
* ending. If so, this function will update the decoder state and pop the stack
* used to memorize endings. This function should be called after updating the
* encodedStreamOffset to the end of decoded SFLV tuple.
*
* @param[in] params - a valid BejHandleTypeFuncParam which contains the decoder
* state.
* @param[in] canBeEmpty - if true, the stack being empty is not an error. If
* false, stack cannot be empty.
* @return 0 if successful.
*/
static int bejProcessEnding(struct BejHandleTypeFuncParam* params,
bool canBeEmpty)
{
if (params->stackCallback->stackEmpty(params->stackDataPtr) && !canBeEmpty)
{
// If bejProcessEnding has been called after adding an appropriate JSON
// property, then stack cannot be empty.
fprintf(stderr, "Ending stack cannot be empty.\n");
return bejErrorUnknown;
}
while (!params->stackCallback->stackEmpty(params->stackDataPtr))
{
const struct BejStackProperty* const ending =
params->stackCallback->stackPeek(params->stackDataPtr);
// Check whether the current offset location matches the expected ending
// offset. If so, we are done with that section.
if (params->state.encodedStreamOffset == ending->streamEndOffset)
{
// Since we are going out of a section, we need to reset the
// dictionary property offsets to this section's parent property
// start.
params->state.mainDictPropOffset = ending->mainDictPropOffset;
params->state.annoDictPropOffset = ending->annoDictPropOffset;
params->state.addPropertyName = ending->addPropertyName;
if (ending->sectionType == bejSectionSet)
{
RETURN_IF_CALLBACK_IERROR(
params->decodedCallback->callbackSetEnd,
params->callbacksDataPtr);
}
else if (ending->sectionType == bejSectionArray)
{
RETURN_IF_CALLBACK_IERROR(
params->decodedCallback->callbackArrayEnd,
params->callbacksDataPtr);
}
params->stackCallback->stackPop(params->stackDataPtr);
}
else
{
RETURN_IF_CALLBACK_IERROR(
params->decodedCallback->callbackPropertyEnd,
params->callbacksDataPtr);
// Do not change the parent dictionary property offset since we are
// still inside the same section.
return 0;
}
}
return 0;
}
/**
* @brief Check whether the current encoded segment being decoded is an array
* element.
*
* @param[in] params - a valid BejHandleTypeFuncParam struct.
* @return true if the encoded segment is an array element. Else false.
*/
static bool bejIsArrayElement(const struct BejHandleTypeFuncParam* params)
{
// If the encoded segment enters an array section, we are adding a
// BejSectionArray to the stack. Therefore if the stack is empty, encoded
// segment cannot be an array element.
if (params->stackCallback->stackEmpty(params->stackDataPtr))
{
return false;
}
const struct BejStackProperty* const ending =
params->stackCallback->stackPeek(params->stackDataPtr);
// If the stack top element holds a BejSectionArray, encoded segment is
// an array element.
return ending->sectionType == bejSectionArray;
}
/**
* @brief Decodes a BejSet type SFLV BEJ tuple.
*
* @param[in] params - a valid BejHandleTypeFuncParam struct.
* @return 0 if successful.
*/
static int bejHandleBejSet(struct BejHandleTypeFuncParam* params)
{
uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber;
// Check whether this BejSet is an array element or not.
if (bejIsArrayElement(params))
{
// Dictionary only contains an entry for element 0.
sequenceNumber = 0;
}
const uint8_t* dictionary;
const struct BejDictionaryProperty* prop;
RETURN_IF_IERROR(
bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
sequenceNumber, &dictionary, &prop));
const char* propName = "";
if (params->state.addPropertyName)
{
propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
prop->nameLength);
}
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetStart,
propName, params->callbacksDataPtr);
// Move the offset to the next SFLV tuple (or end). Make sure that this is
// called before calling bejProcessEnding.
params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
uint64_t elements = bejGetNnint(params->sflv.value);
// If its an empty set, we are done here.
if (elements == 0)
{
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackSetEnd,
params->callbacksDataPtr);
// Since this is an ending of a property (empty array), we should call
// bejProcessEnding. Unless the whole JSON object is an empty set (which
// shouldn't be the case), stack cannot be empty.
bejProcessEnding(params, /*canBeEmpty=*/false);
return 0;
}
// Update the states for the next encoding segment.
struct BejStackProperty newEnding = {
.sectionType = bejSectionSet,
.addPropertyName = params->state.addPropertyName,
.mainDictPropOffset = params->state.mainDictPropOffset,
.annoDictPropOffset = params->state.annoDictPropOffset,
.streamEndOffset = params->sflv.valueEndOffset,
};
RETURN_IF_IERROR(
params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
params->state.addPropertyName = true;
if (params->sflv.tupleS.schema == bejAnnotation)
{
// Since this set is an annotated type, we need to advance the
// annotation dictionary for decoding the next segment.
params->state.annoDictPropOffset = prop->childPointerOffset;
}
else
{
params->state.mainDictPropOffset = prop->childPointerOffset;
}
return 0;
}
/**
* @brief Decodes a BejArray type SFLV BEJ tuple.
*
* @param[in] params - a valid BejHandleTypeFuncParam struct.
* @return 0 if successful.
*/
static int bejHandleBejArray(struct BejHandleTypeFuncParam* params)
{
const uint8_t* dictionary;
const struct BejDictionaryProperty* prop;
RETURN_IF_IERROR(bejGetDictionaryAndProperty(
params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
&dictionary, &prop));
const char* propName = "";
if (params->state.addPropertyName)
{
propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
prop->nameLength);
}
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayStart,
propName, params->callbacksDataPtr);
// Move the offset to the next SFLV tuple (or end). Make sure that this is
// called before calling bejProcessEnding.
params->state.encodedStreamOffset = bejGetFirstTupleOffset(params);
uint64_t elements = bejGetNnint(params->sflv.value);
// If its an empty array, we are done here.
if (elements == 0)
{
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackArrayEnd,
params->callbacksDataPtr);
// Since this is an ending of a property (empty array), we should call
// bejProcessEnding. Stack cannot be empty since there should be at
// least 1 parent in the stack.
bejProcessEnding(params, /*canBeEmpty=*/false);
return 0;
}
// Update the state for next segment decoding.
struct BejStackProperty newEnding = {
.sectionType = bejSectionArray,
.addPropertyName = params->state.addPropertyName,
.mainDictPropOffset = params->state.mainDictPropOffset,
.annoDictPropOffset = params->state.annoDictPropOffset,
.streamEndOffset = params->sflv.valueEndOffset,
};
RETURN_IF_IERROR(
params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
// We do not add property names for array elements.
params->state.addPropertyName = false;
if (params->sflv.tupleS.schema == bejAnnotation)
{
// Since this array is an annotated type, we need to advance the
// annotation dictionary for decoding the next segment.
params->state.annoDictPropOffset = prop->childPointerOffset;
}
else
{
params->state.mainDictPropOffset = prop->childPointerOffset;
}
return 0;
}
/**
* @brief Decodes a BejNull type SFLV BEJ tuple.
*
* @param[in] params - a valid BejHandleTypeFuncParam struct.
* @return 0 if successful.
*/
static int bejHandleBejNull(struct BejHandleTypeFuncParam* params)
{
const char* propName = bejGetPropName(params);
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull, propName,
params->callbacksDataPtr);
params->state.encodedStreamOffset = params->sflv.valueEndOffset;
return bejProcessEnding(params, /*canBeEmpty=*/false);
}
/**
* @brief Decodes a BejInteger type SFLV BEJ tuple.
*
* @param[in] params - a valid BejHandleTypeFuncParam struct.
* @return 0 if successful.
*/
static int bejHandleBejInteger(struct BejHandleTypeFuncParam* params)
{
const char* propName = bejGetPropName(params);
if (params->sflv.valueLength == 0)
{
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
propName, params->callbacksDataPtr);
}
else
{
RETURN_IF_CALLBACK_IERROR(
params->decodedCallback->callbackInteger, propName,
bejGetIntegerValue(params->sflv.value, params->sflv.valueLength),
params->callbacksDataPtr);
}
params->state.encodedStreamOffset = params->sflv.valueEndOffset;
return bejProcessEnding(params, /*canBeEmpty=*/false);
}
/**
* @brief Decodes a BejEnum type SFLV BEJ tuple.
*
* @param[in] params - a valid BejHandleTypeFuncParam struct.
* @return 0 if successful.
*/
static int bejHandleBejEnum(struct BejHandleTypeFuncParam* params)
{
uint16_t sequenceNumber = params->sflv.tupleS.sequenceNumber;
if (bejIsArrayElement(params))
{
sequenceNumber = 0;
}
const uint8_t* dictionary;
const struct BejDictionaryProperty* prop;
RETURN_IF_IERROR(
bejGetDictionaryAndProperty(params, params->sflv.tupleS.schema,
sequenceNumber, &dictionary, &prop));
const char* propName = "";
if (params->state.addPropertyName)
{
propName = bejDictGetPropertyName(dictionary, prop->nameOffset,
prop->nameLength);
}
if (params->sflv.valueLength == 0)
{
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
propName, params->callbacksDataPtr);
}
else
{
// Get the string for enum value.
uint16_t enumValueSequenceN =
(uint16_t)(bejGetNnint(params->sflv.value));
const struct BejDictionaryProperty* enumValueProp;
RETURN_IF_IERROR(
bejDictGetProperty(dictionary, prop->childPointerOffset,
enumValueSequenceN, &enumValueProp));
const char* enumValueName = bejDictGetPropertyName(
dictionary, enumValueProp->nameOffset, enumValueProp->nameLength);
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackEnum,
propName, enumValueName,
params->callbacksDataPtr);
}
// Update the offset to point to the next possible SFLV tuple.
params->state.encodedStreamOffset = params->sflv.valueEndOffset;
return bejProcessEnding(params, /*canBeEmpty=*/false);
}
/**
* @brief Decodes a BejString type SFLV BEJ tuple.
*
* @param[in] params - a valid BejHandleTypeFuncParam struct.
* @return 0 if successful.
*/
static int bejHandleBejString(struct BejHandleTypeFuncParam* params)
{
// TODO: Handle deferred bindings.
const char* propName = bejGetPropName(params);
if (params->sflv.valueLength == 0)
{
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
propName, params->callbacksDataPtr);
}
else
{
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackString,
propName, (const char*)(params->sflv.value),
params->callbacksDataPtr);
}
params->state.encodedStreamOffset = params->sflv.valueEndOffset;
return bejProcessEnding(params, /*canBeEmpty=*/false);
}
/**
* @brief Decodes a BejReal type SFLV BEJ tuple.
*
* @param[in] params - a valid BejHandleTypeFuncParam struct.
* @return 0 if successful.
*/
static int bejHandleBejReal(struct BejHandleTypeFuncParam* params)
{
const char* propName = bejGetPropName(params);
if (params->sflv.valueLength == 0)
{
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
propName, params->callbacksDataPtr);
}
else
{
// Real value has the following format.
// nnint - Length of whole
// bejInteger - whole (includes sign for the overall real number)
// nnint - Leading zero count for fract
// nnint - fract
// nnint - Length of exp
// bejInteger - exp (includes sign for the exponent)
uint8_t wholeByteLen = (uint8_t)bejGetNnint(params->sflv.value);
const uint8_t* wholeBejInt =
params->sflv.value + bejGetNnintSize(params->sflv.value);
const uint8_t* fractZeroCountNnint = wholeBejInt + wholeByteLen;
const uint8_t* fractNnint =
fractZeroCountNnint + bejGetNnintSize(fractZeroCountNnint);
const uint8_t* lenExpNnint = fractNnint + bejGetNnintSize(fractNnint);
const uint8_t* expBejInt = lenExpNnint + bejGetNnintSize(lenExpNnint);
struct BejReal realValue;
realValue.whole = bejGetIntegerValue(wholeBejInt, wholeByteLen);
realValue.zeroCount = bejGetNnint(fractZeroCountNnint);
realValue.fract = bejGetNnint(fractNnint);
realValue.expLen = (uint8_t)bejGetNnint(lenExpNnint);
if (realValue.expLen != 0)
{
realValue.exp = bejGetIntegerValue(
expBejInt, (uint8_t)bejGetNnint(lenExpNnint));
}
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackReal,
propName, &realValue,
params->callbacksDataPtr);
}
params->state.encodedStreamOffset = params->sflv.valueEndOffset;
return bejProcessEnding(params, /*canBeEmpty=*/false);
}
/**
* @brief Decodes a BejBoolean type SFLV BEJ tuple.
*
* @param[in] params - a valid BejHandleTypeFuncParam struct.
* @return 0 if successful.
*/
static int bejHandleBejBoolean(struct BejHandleTypeFuncParam* params)
{
const char* propName = bejGetPropName(params);
if (params->sflv.valueLength == 0)
{
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackNull,
propName, params->callbacksDataPtr);
}
else
{
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackBool,
propName, *(params->sflv.value) > 0,
params->callbacksDataPtr);
}
params->state.encodedStreamOffset = params->sflv.valueEndOffset;
return bejProcessEnding(params, /*canBeEmpty=*/false);
}
/**
* @brief Decodes a BejPropertyAnnotation type SFLV BEJ tuple.
*
* @param[in] params - a valid BejHandleTypeFuncParam struct.
* @return 0 if successful.
*/
static int bejHandleBejPropertyAnnotation(struct BejHandleTypeFuncParam* params)
{
// TODO: Handle colon-delimited string values.
// Property annotation has the form OuterProperty@Annotation. First
// processing the outer property name.
const uint8_t* outerDictionary;
const struct BejDictionaryProperty* outerProp;
RETURN_IF_IERROR(bejGetDictionaryAndProperty(
params, params->sflv.tupleS.schema, params->sflv.tupleS.sequenceNumber,
&outerDictionary, &outerProp));
const char* propName = bejDictGetPropertyName(
outerDictionary, outerProp->nameOffset, outerProp->nameLength);
RETURN_IF_CALLBACK_IERROR(params->decodedCallback->callbackAnnotation,
propName, params->callbacksDataPtr);
// Mark the ending of the property annotation.
struct BejStackProperty newEnding = {
.sectionType = bejSectionNoType,
.addPropertyName = params->state.addPropertyName,
.mainDictPropOffset = params->state.mainDictPropOffset,
.annoDictPropOffset = params->state.annoDictPropOffset,
.streamEndOffset = params->sflv.valueEndOffset,
};
// Update the states for the next encoding segment.
RETURN_IF_IERROR(
params->stackCallback->stackPush(&newEnding, params->stackDataPtr));
params->state.addPropertyName = true;
// We might have to change this for nested annotations.
params->state.mainDictPropOffset = outerProp->childPointerOffset;
// Point to the start of the value for next decoding.
params->state.encodedStreamOffset =
params->sflv.valueEndOffset - params->sflv.valueLength;
return 0;
}
/**
* @brief Decodes an encoded bej stream.
*
* @param[in] schemaDictionary - main schema dictionary to use.
* @param[in] annotationDictionary - annotation dictionary
* @param[in] enStream - encoded stream without the PLDM header.
* @param[in] streamLen - length of the enStream.
* @param[in] stackCallback - callbacks for stack handlers.
* @param[in] decodedCallback - callbacks for extracting decoded properties.
* @param[in] callbacksDataPtr - data pointer to pass to decoded callbacks. This
* can be used pass additional data.
* @param[in] stackDataPtr - data pointer to pass to stack callbacks. This can
* be used pass additional data.
*
* @return 0 if successful.
*/
static int bejDecode(const uint8_t* schemaDictionary,
const uint8_t* annotationDictionary,
const uint8_t* enStream, uint32_t streamLen,
const struct BejStackCallback* stackCallback,
const struct BejDecodedCallback* decodedCallback,
void* callbacksDataPtr, void* stackDataPtr)
{
struct BejHandleTypeFuncParam params = {
.state =
{
// We only add names of set properties. We don't use names for
// array
// properties. Here we are omitting the name of the root set.
.addPropertyName = false,
// At start, parent property from the main dictionary is the
// first property.
.mainDictPropOffset = bejDictGetPropertyHeadOffset(),
.annoDictPropOffset = bejDictGetFirstAnnotatedPropertyOffset(),
// Current location of the encoded segment we are processing.
.encodedStreamOffset = 0,
.encodedSubStream = enStream,
},
.mainDictionary = schemaDictionary,
.annotDictionary = annotationDictionary,
.decodedCallback = decodedCallback,
.stackCallback = stackCallback,
.callbacksDataPtr = callbacksDataPtr,
.stackDataPtr = stackDataPtr,
};
while (params.state.encodedStreamOffset < streamLen)
{
// Go to the next encoded segment in the encoded stream.
params.state.encodedSubStream =
enStream + params.state.encodedStreamOffset;
bejInitSFLVStruct(&params);
if (params.sflv.format.readOnlyProperty)
{
RETURN_IF_CALLBACK_IERROR(
params.decodedCallback->callbackReadonlyProperty,
params.sflv.tupleS.sequenceNumber, params.callbacksDataPtr);
}
// TODO: Handle nullable property types. These are indicated by
// params.sflv.format.nullableProperty
switch (params.sflv.format.principalDataType)
{
case bejSet:
RETURN_IF_IERROR(bejHandleBejSet(&params));
break;
case bejArray:
RETURN_IF_IERROR(bejHandleBejArray(&params));
break;
case bejNull:
RETURN_IF_IERROR(bejHandleBejNull(&params));
break;
case bejInteger:
RETURN_IF_IERROR(bejHandleBejInteger(&params));
break;
case bejEnum:
RETURN_IF_IERROR(bejHandleBejEnum(&params));
break;
case bejString:
RETURN_IF_IERROR(bejHandleBejString(&params));
break;
case bejReal:
RETURN_IF_IERROR(bejHandleBejReal(&params));
break;
case bejBoolean:
RETURN_IF_IERROR(bejHandleBejBoolean(&params));
break;
case bejBytestring:
// TODO: Add support for BejBytestring decoding.
fprintf(stderr, "No BejBytestring support\n");
params.state.encodedStreamOffset = params.sflv.valueEndOffset;
break;
case bejChoice:
// TODO: Add support for BejChoice decoding.
fprintf(stderr, "No BejChoice support\n");
params.state.encodedStreamOffset = params.sflv.valueEndOffset;
break;
case bejPropertyAnnotation:
RETURN_IF_IERROR(bejHandleBejPropertyAnnotation(&params));
break;
case bejResourceLink:
// TODO: Add support for BejResourceLink decoding.
fprintf(stderr, "No BejResourceLink support\n");
params.state.encodedStreamOffset = params.sflv.valueEndOffset;
break;
case bejResourceLinkExpansion:
// TODO: Add support for BejResourceLinkExpansion decoding.
fprintf(stderr, "No BejResourceLinkExpansion support\n");
params.state.encodedStreamOffset = params.sflv.valueEndOffset;
break;
default:
break;
}
}
RETURN_IF_IERROR(bejProcessEnding(&params, /*canBeEmpty=*/true));
if (!params.stackCallback->stackEmpty(params.stackDataPtr))
{
fprintf(stderr, "Ending stack should be empty but its not. Something "
"must have gone wrong with the encoding\n");
return bejErrorUnknown;
}
return 0;
}
/**
* @brief Check if a bej version is supported by this decoder
*
* @param[in] bejVersion - the bej version in the received encoded stream
* @return true if supported.
*/
static bool bejIsSupported(uint32_t bejVersion)
{
for (uint32_t i = 0; i < sizeof(supportedBejVersions) / sizeof(uint32_t);
++i)
{
if (bejVersion == supportedBejVersions[i])
{
return true;
}
}
return false;
}
int bejDecodePldmBlock(const struct BejDictionaries* dictionaries,
const uint8_t* encodedPldmBlock, uint32_t blockLength,
const struct BejStackCallback* stackCallback,
const struct BejDecodedCallback* decodedCallback,
void* callbacksDataPtr, void* stackDataPtr)
{
NULL_CHECK(dictionaries, "dictionaries");
NULL_CHECK(dictionaries->schemaDictionary, "schemaDictionary");
NULL_CHECK(dictionaries->annotationDictionary, "annotationDictionary");
NULL_CHECK(encodedPldmBlock, "encodedPldmBlock");
NULL_CHECK(stackCallback, "stackCallback");
NULL_CHECK(stackCallback->stackEmpty, "stackEmpty");
NULL_CHECK(stackCallback->stackPeek, "stackPeek");
NULL_CHECK(stackCallback->stackPop, "stackPop");
NULL_CHECK(stackCallback->stackPush, "stackPush");
NULL_CHECK(decodedCallback, "decodedCallback");
uint32_t pldmHeaderSize = sizeof(struct BejPldmBlockHeader);
if (blockLength < pldmHeaderSize)
{
fprintf(stderr, "Invalid pldm block size: %u\n", blockLength);
return bejErrorInvalidSize;
}
const struct BejPldmBlockHeader* pldmHeader =
(const struct BejPldmBlockHeader*)encodedPldmBlock;
if (!bejIsSupported(pldmHeader->bejVersion))
{
fprintf(stderr, "Bej decoder doesn't support the bej version: %u\n",
pldmHeader->bejVersion);
return bejErrorNotSuppoted;
}
if (pldmHeader->schemaClass == bejAnnotationSchemaClass)
{
fprintf(stderr,
"Encoder schema class cannot be BejAnnotationSchemaClass\n");
return bejErrorNotSuppoted;
}
// TODO: Add support for CollectionMemberType schema class.
if (pldmHeader->schemaClass == bejCollectionMemberTypeSchemaClass)
{
fprintf(stderr, "Decoder doesn't support "
"bejCollectionMemberTypeSchemaClass yet.\n");
return bejErrorNotSuppoted;
}
// TODO: Add support for Error schema class.
if (pldmHeader->schemaClass == bejErrorSchemaClass)
{
fprintf(stderr, "Decoder doesn't support BejErrorSchemaClass yet.\n");
return bejErrorNotSuppoted;
}
// Skip the PLDM header.
const uint8_t* enStream = encodedPldmBlock + pldmHeaderSize;
uint32_t streamLen = blockLength - pldmHeaderSize;
return bejDecode(dictionaries->schemaDictionary,
dictionaries->annotationDictionary, enStream, streamLen,
stackCallback, decodedCallback, callbacksDataPtr,
stackDataPtr);
}