1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
|
#ifndef AWS_COMMON_CBOR_H
#define AWS_COMMON_CBOR_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/byte_buf.h>
#include <aws/common/common.h>
AWS_PUSH_SANE_WARNING_LEVEL
AWS_EXTERN_C_BEGIN
/**
* The types use by APIs, not 1:1 with major type.
* It's an extension for cbor major type in RFC8949 section 3.1
* Major type 0 - AWS_CBOR_TYPE_UINT
* Major type 1 - AWS_CBOR_TYPE_NEGINT
* Major type 2 - AWS_CBOR_TYPE_BYTES/AWS_CBOR_TYPE_INDEF_BYTES_START
* Major type 3 - AWS_CBOR_TYPE_TEXT/AWS_CBOR_TYPE_INDEF_TEXT_START
* Major type 4 - AWS_CBOR_TYPE_ARRAY_START/AWS_CBOR_TYPE_INDEF_ARRAY_START
* Major type 5 - AWS_CBOR_TYPE_MAP_START/AWS_CBOR_TYPE_INDEF_MAP_START
* Major type 6 - AWS_CBOR_TYPE_TAG
* Major type 7
* - 20/21 - AWS_CBOR_TYPE_BOOL
* - 22 - AWS_CBOR_TYPE_NULL
* - 23 - AWS_CBOR_TYPE_UNDEFINED
* - 25/26/27 - AWS_CBOR_TYPE_FLOAT
* - 31 - AWS_CBOR_TYPE_BREAK
* - rest of value are not supported.
*/
enum aws_cbor_type {
AWS_CBOR_TYPE_UNKNOWN = 0,
AWS_CBOR_TYPE_UINT,
AWS_CBOR_TYPE_NEGINT,
AWS_CBOR_TYPE_FLOAT,
AWS_CBOR_TYPE_BYTES,
AWS_CBOR_TYPE_TEXT,
AWS_CBOR_TYPE_ARRAY_START,
AWS_CBOR_TYPE_MAP_START,
AWS_CBOR_TYPE_TAG,
AWS_CBOR_TYPE_BOOL,
AWS_CBOR_TYPE_NULL,
AWS_CBOR_TYPE_UNDEFINED,
AWS_CBOR_TYPE_BREAK,
AWS_CBOR_TYPE_INDEF_BYTES_START,
AWS_CBOR_TYPE_INDEF_TEXT_START,
AWS_CBOR_TYPE_INDEF_ARRAY_START,
AWS_CBOR_TYPE_INDEF_MAP_START,
};
/**
* The common tags, refer to RFC8949 section 3.4
* Expected value type followed by the tag:
* AWS_CBOR_TAG_STANDARD_TIME - AWS_CBOR_TYPE_TEXT
* AWS_CBOR_TAG_EPOCH_TIME - AWS_CBOR_TYPE_UINT/AWS_CBOR_TYPE_NEGINT/AWS_CBOR_TYPE_FLOAT
* AWS_CBOR_TAG_UNSIGNED_BIGNUM - AWS_CBOR_TYPE_BYTES
* AWS_CBOR_TAG_NEGATIVE_BIGNUM - AWS_CBOR_TYPE_BYTES
* AWS_CBOR_TAG_DECIMAL_FRACTION - AWS_CBOR_TYPE_ARRAY_START/AWS_CBOR_TYPE_INDEF_ARRAY_START
**/
#define AWS_CBOR_TAG_STANDARD_TIME 0
#define AWS_CBOR_TAG_EPOCH_TIME 1
#define AWS_CBOR_TAG_UNSIGNED_BIGNUM 2
#define AWS_CBOR_TAG_NEGATIVE_BIGNUM 3
#define AWS_CBOR_TAG_DECIMAL_FRACTION 4
struct aws_cbor_encoder;
struct aws_cbor_decoder;
/*******************************************************************************
* ENCODE
******************************************************************************/
/* Return c-string for aws_cbor_type */
AWS_COMMON_API
const char *aws_cbor_type_cstr(enum aws_cbor_type type);
/**
* @brief Create a new cbor encoder. Creating a encoder with a temporay buffer.
* Every aws_cbor_encoder_write_* will encode directly into the buffer to follow the encoded data.
*
* @param allocator
* @return aws_cbor_encoder
*/
AWS_COMMON_API
struct aws_cbor_encoder *aws_cbor_encoder_new(struct aws_allocator *allocator);
AWS_COMMON_API
struct aws_cbor_encoder *aws_cbor_encoder_destroy(struct aws_cbor_encoder *encoder);
/**
* @brief Get the current encoded data from encoder. The encoded data has the same lifetime as the encoder, and once
* any other function call invoked for the encoder, the encoded data is no longer valid.
*
* @param encoder
* @return struct aws_byte_cursor from the encoder buffer.
*/
AWS_COMMON_API
struct aws_byte_cursor aws_cbor_encoder_get_encoded_data(const struct aws_cbor_encoder *encoder);
/**
* @brief Clear the current encoded buffer from encoder.
*
* @param encoder
*/
AWS_COMMON_API
void aws_cbor_encoder_reset(struct aws_cbor_encoder *encoder);
/**
* @brief Encode a AWS_CBOR_TYPE_UINT value to "smallest possible" in encoder's buffer.
* Referring to RFC8949 section 4.2.1
*
* TODO: maybe add a width of the encoded value.
*
* @param encoder
* @param value value to encode.
*/
AWS_COMMON_API
void aws_cbor_encoder_write_uint(struct aws_cbor_encoder *encoder, uint64_t value);
/**
* @brief Encode a AWS_CBOR_TYPE_NEGINT value to "smallest possible" in encoder's buffer.
* It represents (-1 - value).
* Referring to RFC8949 section 4.2.1
*
*
* @param encoder
* @param value The argument to encode to negative integer, which is (-1 - expected_val)
*/
AWS_COMMON_API
void aws_cbor_encoder_write_negint(struct aws_cbor_encoder *encoder, uint64_t value);
/**
* @brief Encode a AWS_CBOR_TYPE_FLOAT value to "smallest possible", but will not be encoded into half-precision float,
* as it's not well supported cross languages.
*
* To be more specific, it will be encoded into integer/negative/float
* (Order with priority) when the conversation will not cause precision loss.
*
* @param encoder
* @param value value to encode.
*/
AWS_COMMON_API
void aws_cbor_encoder_write_float(struct aws_cbor_encoder *encoder, double value);
/**
* @brief Encode a AWS_CBOR_TYPE_BYTES value to "smallest possible" in encoder's buffer.
* Referring to RFC8949 section 4.2.1, the length of "from" will be encoded first and then the value of "from" will
* be followed.
*
* @param encoder
* @param from value to encode.
*/
AWS_COMMON_API
void aws_cbor_encoder_write_bytes(struct aws_cbor_encoder *encoder, struct aws_byte_cursor from);
/**
* @brief Encode a AWS_CBOR_TYPE_TEXT value to "smallest possible" in encoder's buffer.
* Referring to RFC8949 section 4.2.1, the length of "from" will be encoded first and then the value of "from" will
* be followed.
*
* @param encoder
* @param from value to encode.
*/
AWS_COMMON_API
void aws_cbor_encoder_write_text(struct aws_cbor_encoder *encoder, struct aws_byte_cursor from);
/**
* @brief Encode a AWS_CBOR_TYPE_ARRAY_START value to "smallest possible" in encoder's buffer.
* Referring to RFC8949 section 4.2.1
* The "number_entries" is the cbor data items should be followed as the content of the array.
* Notes: it's user's responsibility to keep the integrity of the array to be encoded.
*
* @param encoder
* @param number_entries The number of data item in array.
*/
AWS_COMMON_API
void aws_cbor_encoder_write_array_start(struct aws_cbor_encoder *encoder, size_t number_entries);
/**
* @brief Encode a AWS_CBOR_TYPE_MAP_START value to "smallest possible" in encoder's buffer.
* Referring to RFC8949 section 4.2.1
* The "number_entries" is the number of pair of cbor data items as key and value should be followed as the content of
* the map.
*
* Notes: it's user's responsibility to keep the integrity of the map to be encoded.
*
* @param encoder
* @param number_entries The number of data item in map.
*/
AWS_COMMON_API
void aws_cbor_encoder_write_map_start(struct aws_cbor_encoder *encoder, size_t number_entries);
/**
* @brief Encode a AWS_CBOR_TYPE_TAG value to "smallest possible" in encoder's buffer.
* Referring to RFC8949 section 4.2.1
* The following cbor data item will be the content of the tagged value.
* Notes: it's user's responsibility to keep the integrity of the tagged value to follow the RFC8949 section 3.4
*
* @param encoder
* @param tag_number The tag value to encode.
*/
AWS_COMMON_API
void aws_cbor_encoder_write_tag(struct aws_cbor_encoder *encoder, uint64_t tag_number);
/**
* @brief Encode a simple value AWS_CBOR_TYPE_NULL
*
* @param encoder
*/
AWS_COMMON_API
void aws_cbor_encoder_write_null(struct aws_cbor_encoder *encoder);
/**
* @brief Encode a simple value AWS_CBOR_TYPE_UNDEFINED
*
* @param encoder
*/
AWS_COMMON_API
void aws_cbor_encoder_write_undefined(struct aws_cbor_encoder *encoder);
/**
* @brief Encode a simple value AWS_CBOR_TYPE_BOOL
*
* @param encoder
*/
AWS_COMMON_API
void aws_cbor_encoder_write_bool(struct aws_cbor_encoder *encoder, bool value);
/**
* @brief Encode a simple value AWS_CBOR_TYPE_BREAK
*
* Notes: no error checking, it's user's responsibility to track the break
* to close the corresponding indef_start
*/
AWS_COMMON_API
void aws_cbor_encoder_write_break(struct aws_cbor_encoder *encoder);
/**
* @brief Encode a AWS_CBOR_TYPE_INDEF_BYTES_START
*
* Notes: no error checking, it's user's responsibility to add corresponding data and the break
* to close the indef_start
*/
AWS_COMMON_API
void aws_cbor_encoder_write_indef_bytes_start(struct aws_cbor_encoder *encoder);
/**
* @brief Encode a AWS_CBOR_TYPE_INDEF_TEXT_START
*
* Notes: no error checking, it's user's responsibility to add corresponding data
* and the break to close the indef_start
*/
AWS_COMMON_API
void aws_cbor_encoder_write_indef_text_start(struct aws_cbor_encoder *encoder);
/**
* @brief Encode a AWS_CBOR_TYPE_INDEF_ARRAY_START
*
* Notes: no error checking, it's user's responsibility to add corresponding data
* and the break to close the indef_start
*/
AWS_COMMON_API
void aws_cbor_encoder_write_indef_array_start(struct aws_cbor_encoder *encoder);
/**
* @brief Encode a AWS_CBOR_TYPE_INDEF_MAP_START
*
* Notes: no error checking, it's user's responsibility to add corresponding data
* and the break to close the indef_start
*/
AWS_COMMON_API
void aws_cbor_encoder_write_indef_map_start(struct aws_cbor_encoder *encoder);
/*******************************************************************************
* DECODE
******************************************************************************/
/**
* @brief Create a cbor decoder to take src to decode.
* The typical usage of decoder will be:
* - If the next element type only accept what expected, `aws_cbor_decoder_pop_next_*`
* - If the next element type accept different type, invoke `aws_cbor_decoder_peek_type` first, then based on the type
* to invoke corresponding `aws_cbor_decoder_pop_next_*`
* - If the next element type doesn't have corrsponding value, specifically: AWS_CBOR_TYPE_NULL,
* AWS_CBOR_TYPE_UNDEFINED, AWS_CBOR_TYPE_INF_*_START, AWS_CBOR_TYPE_BREAK, call
* `aws_cbor_decoder_consume_next_single_element` to consume it and continues for further decoding.
* - To ignore the next data item (the element and the content of it), `aws_cbor_decoder_consume_next_whole_data_item`
*
* Note: it's caller's responsibilty to keep the src outlive the decoder.
*
* @param allocator
* @param src The src data to decode from.
* @return decoder
*/
AWS_COMMON_API
struct aws_cbor_decoder *aws_cbor_decoder_new(struct aws_allocator *allocator, struct aws_byte_cursor src);
AWS_COMMON_API
struct aws_cbor_decoder *aws_cbor_decoder_destroy(struct aws_cbor_decoder *decoder);
/**
* @brief Get the length of the remaining bytes of the source. Once the source was decoded, it will be consumed,
* and result in decrease of the remaining length of bytes.
*
* @param decoder
* @return The length of bytes remaining of the decoder source.
*/
AWS_COMMON_API
size_t aws_cbor_decoder_get_remaining_length(const struct aws_cbor_decoder *decoder);
/**
* @brief Decode the next element and store it in the decoder cache if there was no element cached.
* If there was element cached, just return the type of the cached element.
*
* @param decoder
* @param out_type
* @return AWS_OP_SUCCESS if succeed, AWS_OP_ERR for any decoding error and corresponding error code will be raised.
*/
AWS_COMMON_API
int aws_cbor_decoder_peek_type(struct aws_cbor_decoder *decoder, enum aws_cbor_type *out_type);
/**
* @brief Consume the next data item, includes all the content within the data item.
*
* As an example for the following cbor, this function will consume all the data
* as it's only one cbor data item, an indefinite map with 2 <key, value> pair:
* 0xbf6346756ef563416d7421ff
* BF -- Start indefinite-length map
* 63 -- First key, UTF-8 string length 3
* 46756e -- "Fun"
* F5 -- First value, true
* 63 -- Second key, UTF-8 string length 3
* 416d74 -- "Amt"
* 21 -- Second value, -2
* FF -- "break"
*
* Notes: this function will not ensure the data item is well-formed.
*
* @param src The src to parse data from
* @return AWS_OP_SUCCESS successfully consumed the next data item, otherwise AWS_OP_ERR.
*/
AWS_COMMON_API
int aws_cbor_decoder_consume_next_whole_data_item(struct aws_cbor_decoder *decoder);
/**
* @brief Consume the next single element, without the content followed by the element.
*
* As an example for the following cbor, this function will only consume the
* 0xBF, "Start indefinite-length map", not any content of the map represented.
* The next element to decode will start from 0x63
* 0xbf6346756ef563416d7421ff
* BF -- Start indefinite-length map
* 63 -- First key, UTF-8 string length 3
* 46756e -- "Fun"
* F5 -- First value, true
* 63 -- Second key, UTF-8 string length 3
* 416d74 -- "Amt"
* 21 -- Second value, -2
* FF -- "break"
*
* @param decoder The decoder to parse data from
* @return AWS_OP_SUCCESS successfully consumed the next element, otherwise AWS_OP_ERR.
*/
AWS_COMMON_API
int aws_cbor_decoder_consume_next_single_element(struct aws_cbor_decoder *decoder);
/**
* @brief Get the next element based on the type. If the next element doesn't match the expected type. Error will be
* raised. If the next element already been cached, it will consume the cached item when no error was returned.
* Specifically:
* AWS_CBOR_TYPE_UINT - aws_cbor_decoder_pop_next_unsigned_int_val
* AWS_CBOR_TYPE_NEGINT - aws_cbor_decoder_pop_next_negative_int_val, it represents (-1 - *out)
* AWS_CBOR_TYPE_FLOAT - aws_cbor_decoder_pop_next_float_val
* AWS_CBOR_TYPE_BYTES - aws_cbor_decoder_pop_next_bytes_val
* AWS_CBOR_TYPE_TEXT - aws_cbor_decoder_pop_next_text_val
*
* @param decoder
* @param out
* @return AWS_OP_SUCCESS successfully consumed the next element and get the result, otherwise AWS_OP_ERR.
*/
AWS_COMMON_API
int aws_cbor_decoder_pop_next_unsigned_int_val(struct aws_cbor_decoder *decoder, uint64_t *out);
AWS_COMMON_API
int aws_cbor_decoder_pop_next_negative_int_val(struct aws_cbor_decoder *decoder, uint64_t *out);
AWS_COMMON_API
int aws_cbor_decoder_pop_next_float_val(struct aws_cbor_decoder *decoder, double *out);
AWS_COMMON_API
int aws_cbor_decoder_pop_next_boolean_val(struct aws_cbor_decoder *decoder, bool *out);
AWS_COMMON_API
int aws_cbor_decoder_pop_next_bytes_val(struct aws_cbor_decoder *decoder, struct aws_byte_cursor *out);
AWS_COMMON_API
int aws_cbor_decoder_pop_next_text_val(struct aws_cbor_decoder *decoder, struct aws_byte_cursor *out);
/**
* @brief Get the next AWS_CBOR_TYPE_ARRAY_START element. Only consume the AWS_CBOR_TYPE_ARRAY_START element and set the
* size of array to *out_size, not the content of the array. The next *out_size cbor data items will be the content of
* the array for a valid cbor data,
*
* Notes: For indefinite-length, this function will fail with "AWS_ERROR_CBOR_UNEXPECTED_TYPE". The designed way to
* handle indefinite-length is:
* - Get AWS_CBOR_TYPE_INDEF_ARRAY_START from _peek_type
* - call `aws_cbor_decoder_consume_next_single_element` to pop the indefinite-length start.
* - Decode the next data item until AWS_CBOR_TYPE_BREAK read.
*
* @param decoder
* @param out_size store the size of array if succeed.
* @return AWS_OP_SUCCESS successfully consumed the next element and get the result, otherwise AWS_OP_ERR.
*/
AWS_COMMON_API
int aws_cbor_decoder_pop_next_array_start(struct aws_cbor_decoder *decoder, uint64_t *out_size);
/**
* @brief Get the next AWS_CBOR_TYPE_MAP_START element. Only consume the AWS_CBOR_TYPE_MAP_START element and set the
* size of array to *out_size, not the content of the map. The next *out_size pair of cbor data items as key and value
* will be the content of the array for a valid cbor data,
*
* Notes: For indefinite-length, this function will fail with "AWS_ERROR_CBOR_UNEXPECTED_TYPE". The designed way to
* handle indefinite-length is:
* - Get AWS_CBOR_TYPE_INDEF_MAP_START from _peek_type
* - call `aws_cbor_decoder_consume_next_single_element` to pop the indefinite-length start.
* - Decode the next data item until AWS_CBOR_TYPE_BREAK read.
*
* @param decoder
* @param out_size store the size of map if succeed.
* @return AWS_OP_SUCCESS successfully consumed the next element and get the result, otherwise AWS_OP_ERR.
*/
AWS_COMMON_API
int aws_cbor_decoder_pop_next_map_start(struct aws_cbor_decoder *decoder, uint64_t *out_size);
/**
* @brief Get the next AWS_CBOR_TYPE_TAG element. Only consume the AWS_CBOR_TYPE_TAG element and set the
* tag value to *out_tag_val, not the content of the tagged. The next cbor data item will be the content of the tagged
* value for a valid cbor data.
*
* @param decoder
* @param out_size store the size of map if succeed.
* @return AWS_OP_SUCCESS successfully consumed the next element and get the result, otherwise AWS_OP_ERR.
*/
AWS_COMMON_API
int aws_cbor_decoder_pop_next_tag_val(struct aws_cbor_decoder *decoder, uint64_t *out_tag_val);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif // AWS_COMMON_CBOR_H
|