| #include "bej_decoder_json.hpp" |
| |
| #include "bej_common.h" |
| #include "bej_decoder_core.h" |
| |
| #include "bej_deferred_binding_format.hpp" |
| |
| #include <array> |
| #include <charconv> |
| #include <cstdio> |
| #include <cstring> |
| #include <format> |
| #include <limits> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| |
| #define MAX_BEJ_STRING_LEN 65536 |
| |
| namespace libbej |
| { |
| |
| /** |
| * @brief This structure is used to pass additional data to callback functions. |
| */ |
| struct BejJsonParam |
| { |
| bool* isPrevAnnotated; |
| std::string* output; |
| const BejDeferredBindingMap* bindings; |
| }; |
| |
| /** |
| * @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 Parse a base-10 integer at str[start] without throwing. |
| * |
| * @param[in] str - the string to parse from (start must be <= str.length()). |
| * @param[in] start - index to begin parsing. |
| * @param[out] out - parsed value, valid only on success. |
| * @param[out] end - index just past the last parsed digit. |
| * @return true if at least one digit was parsed and the value fit in T. |
| */ |
| template <typename T> |
| static bool parseDecimalAt(const std::string& str, size_t start, T& out, |
| size_t& end) |
| { |
| auto result = |
| std::from_chars(str.data() + start, str.data() + str.length(), out); |
| end = static_cast<size_t>(result.ptr - str.data()); |
| return result.ec == std::errc{}; |
| } |
| |
| // DSP0218 Table 42 markers supported by this decoder, modelled as data: |
| // - a marker fitting an existing arity is just a new table row. |
| // - a marker with a new operand shape (e.g. %P<id>.PAGE<offset>) also needs a |
| // new BejMacroArity value and a parse branch below. |
| namespace |
| { |
| |
| // How a marker's trailing parameters are parsed, which also fixes the token |
| // boundary. |
| enum class BejMacroArity : uint8_t |
| { |
| none, // %C, %S: the token is just the marker text |
| resourceId, // %L<id>, %I<id>: marker + decimal id |
| resourceIdDotAction, // %T<id>.<action>: marker + decimal id + '.' + action |
| }; |
| |
| // Shape of the DSP0218 Table 42 "invalid" form, emitted when resolution is |
| // enabled (a non-empty map) but the placeholder cannot be resolved. |
| enum class BejInvalidKind : uint8_t |
| { |
| none, // %C, %S: spec defines no invalid form; leave the token raw |
| resourcePdr, // %L<id> -> "/invalid.PDR<id>" |
| literal, // %I<id> -> "invalid" |
| action, // %T<id>.<n> -> "/invalid.<id>.<n>" |
| }; |
| |
| // A marker is matched as text, not a single character, so the table can hold |
| // multi-symbol markers such as "%PD": |
| // - findMacroSpec recognizes it; longest-match wins over a shorter prefix. |
| // - each new operand shape needs its own BejMacroArity and invalid-form. |
| struct BejMacroSpec |
| { |
| std::string_view marker; |
| BejMacroArity arity; |
| BejInvalidKind invalid; |
| }; |
| |
| // Wrap a single-character marker constant as a string_view, so the table reuses |
| // the marker constants instead of duplicating the character literals. |
| constexpr std::string_view asMarker(const char& marker) |
| { |
| return std::string_view(&marker, 1); |
| } |
| |
| constexpr auto bejMacroSpecs = std::to_array<BejMacroSpec>({ |
| {asMarker(bejDeferredBindingResourceLink), BejMacroArity::resourceId, |
| BejInvalidKind::resourcePdr}, |
| {asMarker(bejDeferredBindingInstanceId), BejMacroArity::resourceId, |
| BejInvalidKind::literal}, |
| {asMarker(bejDeferredBindingActionUri), BejMacroArity::resourceIdDotAction, |
| BejInvalidKind::action}, |
| {asMarker(bejDeferredBindingChassis), BejMacroArity::none, |
| BejInvalidKind::none}, |
| {asMarker(bejDeferredBindingSystem), BejMacroArity::none, |
| BejInvalidKind::none}, |
| }); |
| |
| constexpr std::string_view bejInvalidPdrPrefix = "/invalid.PDR"; |
| constexpr std::string_view bejInvalidLiteral = "invalid"; |
| constexpr std::string_view bejInvalidActionPrefix = "/invalid."; |
| |
| // Find the marker whose text matches str at markerStart. Longest match wins, so |
| // a multi-symbol marker is preferred over a single-symbol prefix of it. |
| const BejMacroSpec* findMacroSpec(std::string_view str, size_t markerStart) |
| { |
| const BejMacroSpec* match = nullptr; |
| for (const BejMacroSpec& spec : bejMacroSpecs) |
| { |
| if (str.compare(markerStart, spec.marker.length(), spec.marker) != 0) |
| { |
| continue; |
| } |
| if (match == nullptr || spec.marker.length() > match->marker.length()) |
| { |
| match = &spec; |
| } |
| } |
| return match; |
| } |
| |
| // Parse the placeholder body that begins at the marker text (one past the '%'). |
| // On success, bodyEnd is the index just past the token, and resourceId / |
| // actionId hold the parsed numbers per the marker's arity. Returns false for a |
| // malformed body, which the caller leaves raw. |
| bool parseMacroBody(const std::string& str, size_t markerStart, |
| const BejMacroSpec& spec, size_t& bodyEnd, |
| uint32_t& resourceId, uint32_t& actionId) |
| { |
| // Trailing parameters follow the marker text; a multi-symbol marker just |
| // shifts where they begin. |
| size_t paramStart = markerStart + spec.marker.length(); |
| if (spec.arity == BejMacroArity::none) |
| { |
| bodyEnd = paramStart; |
| return true; |
| } |
| // from_chars reports overflow and "no digits" via its return, unlike |
| // std::stoul whose exception would unwind across the C decoder frames. |
| size_t idEnd = 0; |
| if (!parseDecimalAt(str, paramStart, resourceId, idEnd)) |
| { |
| return false; |
| } |
| if (spec.arity == BejMacroArity::resourceId) |
| { |
| bodyEnd = idEnd; |
| return true; |
| } |
| // resourceIdDotAction requires a ".<action>" suffix. |
| if (idEnd >= str.length() || |
| str[idEnd] != bejDeferredBindingActionSeparator) |
| { |
| return false; |
| } |
| size_t actEnd = 0; |
| if (!parseDecimalAt(str, idEnd + 1, actionId, actEnd)) |
| { |
| return false; |
| } |
| bodyEnd = actEnd; |
| return true; |
| } |
| |
| // Build the DSP0218 Table 42 invalid form for an unresolved placeholder. |
| // Returns false when the marker has no invalid form (%C, %S), leaving it raw. |
| bool buildInvalidForm(const BejMacroSpec& spec, uint32_t resourceId, |
| uint32_t actionId, std::string& out) |
| { |
| switch (spec.invalid) |
| { |
| case BejInvalidKind::resourcePdr: |
| out = std::format("{}{}", bejInvalidPdrPrefix, resourceId); |
| return true; |
| case BejInvalidKind::literal: |
| out = std::string(bejInvalidLiteral); |
| return true; |
| case BejInvalidKind::action: |
| out = std::format("{}{}{}{}", bejInvalidActionPrefix, resourceId, |
| bejDeferredBindingActionSeparator, actionId); |
| return true; |
| case BejInvalidKind::none: |
| return false; |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| /** |
| * @brief Substitute deferred binding placeholders in a string and append the |
| * result to the output. |
| */ |
| static void addDeferredBindingString(struct BejJsonParam* params, |
| const char* value, size_t length) |
| { |
| if (length == 0) |
| { |
| return; |
| } |
| if (!params->bindings || params->bindings->empty()) |
| { |
| // Resolution disabled: emit the raw string unchanged. |
| params->output->append(value, length - 1); |
| return; |
| } |
| |
| std::string str(value, length - 1); |
| size_t pos = 0; |
| while ((pos = str.find(bejDeferredBindingMarker, pos)) != std::string::npos) |
| { |
| if (pos + 1 >= str.length()) |
| { |
| // A lone '%' at the end of the string: no marker can follow. |
| break; |
| } |
| if (str[pos + 1] == bejDeferredBindingMarker) |
| { |
| // Escaped marker. Replace "%%" with "%". |
| str.replace(pos, 2, 1, bejDeferredBindingMarker); |
| pos++; |
| continue; |
| } |
| |
| const BejMacroSpec* spec = findMacroSpec(str, pos + 1); |
| if (spec == nullptr) |
| { |
| // Unsupported marker: DSP0218 Table 42 passes it through unchanged. |
| pos++; |
| continue; |
| } |
| |
| size_t bodyEnd = 0; |
| uint32_t resourceId = 0; |
| uint32_t actionId = 0; |
| if (!parseMacroBody(str, pos + 1, *spec, bodyEnd, resourceId, actionId)) |
| { |
| // Malformed body: leave the placeholder raw. |
| pos++; |
| continue; |
| } |
| |
| // The lookup token is the placeholder body (e.g. "L30", "T30.1", "C"). |
| std::string token = str.substr(pos + 1, bodyEnd - (pos + 1)); |
| std::string replacement; |
| auto it = params->bindings->find(token); |
| if (it != params->bindings->end() && !it->second.empty()) |
| { |
| replacement = it->second; |
| } |
| else if (!buildInvalidForm(*spec, resourceId, actionId, replacement)) |
| { |
| // No binding and no invalid form (%C, %S): leave the token raw. |
| pos = bodyEnd; |
| continue; |
| } |
| str.replace(pos, bodyEnd - pos, replacement); |
| pos += replacement.length(); |
| } |
| params->output->append(str); |
| } |
| |
| /** |
| * @brief Callback for bejString type. |
| * |
| * @param[in] propertyName - a NULL terminated string. |
| * @param[in] value - a NULL terminated string. |
| * @param[in] length - length of the string. |
| * @param[in] deferredBinding - indicates if string contains deferred binding |
| * placeholders. |
| * @param[in] dataPtr - pointing to a valid BejJsonParam struct. |
| * @return 0 if successful. |
| */ |
| static int callbackString(const char* propertyName, const char* value, |
| size_t length, bool deferredBinding, void* dataPtr) |
| { |
| if ((length > MAX_BEJ_STRING_LEN) || |
| (strnlen(value, length) != (length - 1))) |
| { |
| fprintf(stderr, |
| "Incorrect BEJ string length %zu or it exceeds maximum %u.\n", |
| (length - 1), MAX_BEJ_STRING_LEN); |
| return bejErrorInvalidSize; |
| } |
| struct BejJsonParam* params = |
| reinterpret_cast<struct BejJsonParam*>(dataPtr); |
| addPropertyNameToOutput(params, propertyName); |
| params->output->push_back('\"'); |
| if (length > 0) |
| { |
| if (deferredBinding) |
| { |
| addDeferredBindingString(params, value, length); |
| } |
| else |
| { |
| params->output->append(value, length - 1); |
| } |
| } |
| 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); |
| |
| // Sanity check for zeroCount |
| if (value->zeroCount > 100) |
| { |
| return bejErrorInvalidSize; |
| } |
| |
| 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 bejResourceLink type. |
| * |
| * @param[in] propertyName - a NULL terminated string. |
| * @param[in] linkId - Resource Link ID. |
| * @param[in] dataPtr - pointing to a valid BejJsonParam struct. |
| * @return 0 if successful. |
| */ |
| static int callbackResourceLink(const char* propertyName, uint64_t linkId, |
| void* dataPtr) |
| { |
| struct BejJsonParam* params = |
| reinterpret_cast<struct BejJsonParam*>(dataPtr); |
| addPropertyNameToOutput(params, propertyName); |
| *params->isPrevAnnotated = false; |
| |
| // bejResourceLink carries a numeric resource id, not a placeholder string, |
| // so it resolves here rather than inside addDeferredBindingString. |
| const bool resolutionEnabled = |
| params->bindings && !params->bindings->empty(); |
| if (!resolutionEnabled) |
| { |
| // Resolution disabled (no bindings supplied): preserve the raw %L<id> |
| // placeholder, matching decode() called without a map. |
| params->output->append( |
| std::format("\"{}{}{}\"", bejDeferredBindingMarker, |
| bejDeferredBindingResourceLink, linkId)); |
| return 0; |
| } |
| |
| // Resource ids are 32-bit; a link id beyond that range cannot name a |
| // resource, so it never matches and falls through to the invalid form |
| // rather than being truncated into a 32-bit token. |
| if (linkId <= std::numeric_limits<uint32_t>::max()) |
| { |
| auto it = params->bindings->find( |
| bejBinding::resourceLink(static_cast<uint32_t>(linkId))); |
| if (it != params->bindings->end() && !it->second.empty()) |
| { |
| params->output->append(std::format("\"{}\"", it->second)); |
| return 0; |
| } |
| } |
| |
| // Enabled but unrecognized (or out of 32-bit range): %L invalid form. |
| params->output->append( |
| std::format("\"{}{}\"", bejInvalidPdrPrefix, linkId)); |
| 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, |
| const BejDeferredBindingMap& bindings) |
| { |
| // 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, |
| .callbackResourceLink = callbackResourceLink, |
| .callbackReadonlyPropertyAndTopLevelAnnotation = nullptr, |
| }; |
| |
| isPrevAnnotated = false; |
| struct BejJsonParam callbackData = { |
| .isPrevAnnotated = &isPrevAnnotated, |
| .output = &output, |
| .bindings = &bindings, |
| }; |
| |
| return bejDecodePldmBlockWithPolicy( |
| &dictionaries, encodedPldmBlock.data(), encodedPldmBlock.size_bytes(), |
| &stackCallback, &decodedCallback, (void*)(&callbackData), |
| (void*)(&stack), trailingPolicy); |
| } |
| |
| std::string BejDecoderJson::getOutput() |
| { |
| return output; |
| } |
| |
| } // namespace libbej |