450 lines
16 KiB
C
450 lines
16 KiB
C
#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_double_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
|