blob: ab1f6d636dca8572642457752a8797a750b46d58 [file] [log] [blame]
kasunath64cb1972022-05-13 12:54:23 -07001#include "bej_decoder_core.h"
2
3#include "bej_dictionary.h"
4#include "stdio.h"
5
6#include <inttypes.h>
7#include <stdbool.h>
8#include <stdlib.h>
9#include <string.h>
10
11// TODO: Support nested annotations for version 0xF1F1F000
12const uint32_t supportedBejVersions[] = {0xF1F0F000};
13
14/**
15 * @brief Call a callback function. If the callback function is NULL, this will
16 * not do anything. If the callback function returns a non-zero value, this will
17 * cause the caller to return with the non-zero status.
18 */
19#define RETURN_IF_CALLBACK_IERROR(function, ...) \
20 do \
21 { \
22 if ((function) != NULL) \
23 { \
24 int __status = ((function)(__VA_ARGS__)); \
25 if (__status != 0) \
26 { \
27 return __status; \
28 } \
29 } \
30 } while (0)
31
32/**
33 * @brief Get offsets of SFLV fields with respect to the enSegment start.
34 *
35 * @param[in] enSegment - a valid pointer to a start of a SFLV bejTuple.
36 * @param[out] offsets - this will hold the local offsets.
37 */
38static void bejGetLocalBejSFLVOffsets(const uint8_t* enSegment,
39 struct BejSFLVOffset* offsets)
40{
41 // Structure of the SFLV.
42 // [Number of bytes need to represent the sequence number] - uint8_t
43 // [SequenceNumber] - multi byte
44 // [Format] - uint8_t
45 // [Number of bytes need to represent the value length] - uint8_t
46 // [Value length] - multi byte
47
48 // Number of bytes need to represent the sequence number.
49 const uint8_t seqSize = *enSegment;
50 // Start of format.
51 const uint32_t formatOffset = sizeof(uint8_t) + seqSize;
52 // Start of length of the value-length bytes.
53 const uint32_t valueLenNnintOffset = formatOffset + sizeof(uint8_t);
54 // Number of bytes need to represent the value length.
55 const uint8_t valueLengthSize = *(enSegment + valueLenNnintOffset);
56 // Start of the Value.
57 const uint32_t valueOffset =
58 valueLenNnintOffset + sizeof(uint8_t) + valueLengthSize;
59
60 offsets->formatOffset = formatOffset;
61 offsets->valueLenNnintOffset = valueLenNnintOffset;
62 offsets->valueOffset = valueOffset;
63}
64
65/**
66 * @brief Initialize sflv struct in params struct.
67 *
68 * @param[inout] params - a valid BejHandleTypeFuncParam struct with
69 * params->state.encodedSubStream pointing to the start of the encoded stream
70 * and params->state.encodedStreamOffset pointing to the current bejTuple.
71 */
72static void bejInitSFLVStruct(struct BejHandleTypeFuncParam* params)
73{
74 struct BejSFLVOffset localOffset;
75 // Get offsets of different SFLV fields with respect to start of the encoded
76 // segment.
77 bejGetLocalBejSFLVOffsets(params->state.encodedSubStream, &localOffset);
78 struct BejSFLV* sflv = &params->sflv;
79 const uint32_t valueLength = (uint32_t)(rdeGetNnint(
80 params->state.encodedSubStream + localOffset.valueLenNnintOffset));
81 // Sequence number itself should be 16bits. Using 32bits for
82 // [sequence_number + schema_type].
83 uint32_t tupleS = (uint32_t)(rdeGetNnint(params->state.encodedSubStream));
84 sflv->tupleS.schema = (uint8_t)(tupleS & DICTIONARY_TYPE_MASK);
85 sflv->tupleS.sequenceNumber =
86 (uint16_t)((tupleS & (~DICTIONARY_TYPE_MASK)) >>
87 DICTIONARY_SEQ_NUM_SHIFT);
88 sflv->format = *(struct BejTupleF*)(params->state.encodedSubStream +
89 localOffset.formatOffset);
90 sflv->valueLength = valueLength;
91 sflv->valueEndOffset = params->state.encodedStreamOffset +
92 localOffset.valueOffset + valueLength;
93 sflv->value = params->state.encodedSubStream + localOffset.valueOffset;
94}
95
96/**
97 * @brief Decodes an encoded bej stream.
98 *
99 * @param[in] schemaDictionary - main schema dictionary to use.
100 * @param[in] annotationDictionary - annotation dictionary
101 * @param[in] enStream - encoded stream without the PLDM header.
102 * @param[in] streamLen - length of the enStream.
103 * @param[in] stackCallback - callbacks for stack handlers.
104 * @param[in] decodedCallback - callbacks for extracting decoded properties.
105 * @param[in] callbacksDataPtr - data pointer to pass to decoded callbacks. This
106 * can be used pass additional data.
107 * @param[in] stackDataPtr - data pointer to pass to stack callbacks. This can
108 * be used pass additional data.
109 *
110 * @return 0 if successful.
111 */
112static int bejDecode(const uint8_t* schemaDictionary,
113 const uint8_t* annotationDictionary,
114 const uint8_t* enStream, uint32_t streamLen,
115 const struct BejStackCallback* stackCallback,
116 const struct BejDecodedCallback* decodedCallback,
117 void* callbacksDataPtr, void* stackDataPtr)
118{
119 struct BejHandleTypeFuncParam params = {
120 .state =
121 {
122 // We only add names of set properties. We don't use names for
123 // array
124 // properties. Here we are omitting the name of the root set.
125 .addPropertyName = false,
126 // At start, parent property from the main dictionary is the
127 // first property.
128 .mainDictPropOffset = bejDictGetPropertyHeadOffset(),
129 .annoDictPropOffset = bejDictGetFirstAnnotatedPropertyOffset(),
130 // Current location of the encoded segment we are processing.
131 .encodedStreamOffset = 0,
132 .encodedSubStream = enStream,
133 },
134 .mainDictionary = schemaDictionary,
135 .annotDictionary = annotationDictionary,
136 .decodedCallback = decodedCallback,
137 .stackCallback = stackCallback,
138 .callbacksDataPtr = callbacksDataPtr,
139 .stackDataPtr = stackDataPtr,
140 };
141
142 while (params.state.encodedStreamOffset < streamLen)
143 {
144 // Go to the next encoded segment in the encoded stream.
145 params.state.encodedSubStream =
146 enStream + params.state.encodedStreamOffset;
147 bejInitSFLVStruct(&params);
148
149 if (params.sflv.format.readOnlyProperty)
150 {
151 RETURN_IF_CALLBACK_IERROR(
152 params.decodedCallback->callbackReadonlyProperty,
153 params.sflv.tupleS.sequenceNumber, params.callbacksDataPtr);
154 }
155
156 // TODO: Handle nullable property types. These are indicated by
157 // params.sflv.format.nullableProperty
158 switch (params.sflv.format.principalDataType)
159 {
160 case bejSet:
161 // TODO: Add support for BejSet decoding.
162 fprintf(stderr, "No BejSet support\n");
163 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
164 break;
165 case bejArray:
166 // TODO: Add support for BejArray decoding.
167 fprintf(stderr, "No BejArray support\n");
168 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
169 break;
170 case bejNull:
171 // TODO: Add support for BejNull decoding.
172 fprintf(stderr, "No BejNull support\n");
173 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
174 break;
175 case bejInteger:
176 // TODO: Add support for BejInteger decoding.
177 fprintf(stderr, "No BejInteger support\n");
178 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
179 break;
180 case bejEnum:
181 // TODO: Add support for BejEnum decoding.
182 fprintf(stderr, "No BejEnum support\n");
183 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
184 break;
185 case bejString:
186 // TODO: Add support for BejString decoding.
187 fprintf(stderr, "No BejString support\n");
188 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
189 break;
190 case bejReal:
191 // TODO: Add support for BejReal decoding.
192 fprintf(stderr, "No BejReal support\n");
193 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
194 break;
195 case bejBoolean:
196 // TODO: Add support for BejBoolean decoding.
197 fprintf(stderr, "No BejBoolean support\n");
198 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
199 break;
200 case bejBytestring:
201 // TODO: Add support for BejBytestring decoding.
202 fprintf(stderr, "No BejBytestring support\n");
203 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
204 break;
205 case bejChoice:
206 // TODO: Add support for BejChoice decoding.
207 fprintf(stderr, "No BejChoice support\n");
208 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
209 break;
210 case bejPropertyAnnotation:
211 // TODO: Add support for BejPropertyAnnotation decoding.
212 fprintf(stderr, "No BejPropertyAnnotation support\n");
213 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
214 break;
215 case bejResourceLink:
216 // TODO: Add support for BejResourceLink decoding.
217 fprintf(stderr, "No BejResourceLink support\n");
218 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
219 break;
220 case bejResourceLinkExpansion:
221 // TODO: Add support for BejResourceLinkExpansion decoding.
222 fprintf(stderr, "No BejResourceLinkExpansion support\n");
223 params.state.encodedStreamOffset = params.sflv.valueEndOffset;
224 break;
225 default:
226 break;
227 }
228 }
229 // TODO: Enable this once we are handling different data types.
230 // RETURN_IF_IERROR(bejProcessEnding(&params, /*canBeEmpty=*/true));
231 if (!params.stackCallback->stackEmpty(params.stackDataPtr))
232 {
233 fprintf(stderr, "Ending stack should be empty but its not. Something "
234 "must have gone wrong with the encoding\n");
235 return bejErrorUnknown;
236 }
237 return 0;
238}
239
240/**
241 * @brief Check if a bej version is supported by this decoder
242 *
243 * @param bejVersion[in] - the bej version in the received encoded stream
244 * @return true if supported.
245 */
246static bool bejIsSupported(uint32_t bejVersion)
247{
248 for (uint32_t i = 0; i < sizeof(supportedBejVersions) / sizeof(uint32_t);
249 ++i)
250 {
251 if (bejVersion == supportedBejVersions[i])
252 {
253 return true;
254 }
255 }
256 return false;
257}
258
259int bejDecodePldmBlock(const struct BejDictionaries* dictionaries,
260 const uint8_t* encodedPldmBlock, uint32_t blockLength,
261 const struct BejStackCallback* stackCallback,
262 const struct BejDecodedCallback* decodedCallback,
263 void* callbacksDataPtr, void* stackDataPtr)
264{
265 uint32_t pldmHeaderSize = sizeof(struct BejPldmBlockHeader);
266 if (blockLength < pldmHeaderSize)
267 {
268 fprintf(stderr, "Invalid pldm block size: %u\n", blockLength);
269 return bejErrorInvalidSize;
270 }
271
272 const struct BejPldmBlockHeader* pldmHeader =
273 (const struct BejPldmBlockHeader*)encodedPldmBlock;
274
275 if (!bejIsSupported(pldmHeader->bejVersion))
276 {
277 fprintf(stderr, "Bej decoder doesn't support the bej version: %u\n",
278 pldmHeader->bejVersion);
279 return bejErrorNotSuppoted;
280 }
281
282 if (pldmHeader->schemaClass == bejAnnotationSchemaClass)
283 {
284 fprintf(stderr,
285 "Encoder schema class cannot be BejAnnotationSchemaClass\n");
286 return bejErrorNotSuppoted;
287 }
288 // TODO: Add support for CollectionMemberType schema class.
289 if (pldmHeader->schemaClass == bejCollectionMemberTypeSchemaClass)
290 {
291 fprintf(stderr, "Decoder doesn't support "
292 "BejCollectionMemberTypeSchemaClass yet.\n");
293 return bejErrorNotSuppoted;
294 }
295 // TODO: Add support for Error schema class.
296 if (pldmHeader->schemaClass == bejErrorSchemaClass)
297 {
298 fprintf(stderr, "Decoder doesn't support BejErrorSchemaClass yet.\n");
299 return bejErrorNotSuppoted;
300 }
301
302 // Skip the PLDM header.
303 const uint8_t* enStream = encodedPldmBlock + pldmHeaderSize;
304 uint32_t streamLen = blockLength - pldmHeaderSize;
305 return bejDecode(dictionaries->schemaDictionary,
306 dictionaries->annotationDictionary, enStream, streamLen,
307 stackCallback, decodedCallback, callbacksDataPtr,
308 stackDataPtr);
309}