From da3c3a736faeaa98909d62594f7c1a92fafdc213 Mon Sep 17 00:00:00 2001 From: Dengke Date: Wed, 12 Jun 2024 15:52:08 -0700 Subject: [PATCH 1/9] cbor binding --- CMakeLists.txt | 14 +- crt/aws-c-common | 2 +- include/aws/crt/cbor/Cbor.h | 364 ++++++++++++++++++++++++++++++++++++ source/cbor/Cbor.cpp | 209 +++++++++++++++++++++ tests/CMakeLists.txt | 2 + tests/CborTest.cpp | 36 ++++ 6 files changed, 621 insertions(+), 6 deletions(-) create mode 100644 include/aws/crt/cbor/Cbor.h create mode 100644 source/cbor/Cbor.cpp create mode 100644 tests/CborTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ec6d17202..7623aed10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,8 +171,8 @@ file(GLOB AWS_CRT_ENDPOINT_HEADERS "include/aws/crt/endpoints/*.h" ) -file(GLOB AWS_CRT_EXTERNAL_HEADERS - "include/aws/crt/external/*.h" +file(GLOB AWS_CRT_CBOR_HEADERS + "include/aws/crt/cbor/*.h" ) file(GLOB AWS_CRT_PUBLIC_HEADERS @@ -184,6 +184,7 @@ file(GLOB AWS_CRT_PUBLIC_HEADERS ${AWS_CRT_MQTT_HEADERS} ${AWS_CRT_HTTP_HEADERS} ${AWS_CRT_ENDPOINT_HEADERS} + ${AWS_CRT_CBOR_HEADERS} ) if(BUILD_DEPS) @@ -228,8 +229,8 @@ file(GLOB AWS_CRT_ENDPOINTS_SRC "source/endpoints/*.cpp" ) -file(GLOB AWS_CRT_EXTERNAL_SRC - "source/external/*.cpp" +file(GLOB AWS_CRT_CBOR_SRC + "source/cbor/*.cpp" ) file(GLOB AWS_CRT_CPP_SRC @@ -241,7 +242,7 @@ file(GLOB AWS_CRT_CPP_SRC ${AWS_CRT_MQTT_SRC} ${AWS_CRT_HTTP_SRC} ${AWS_CRT_ENDPOINTS_SRC} - ${AWS_CRT_EXTERNAL_SRC} + ${AWS_CRT_CBOR_SRC} ) if(WIN32) @@ -254,6 +255,7 @@ if(WIN32) source_group("Header Files\\aws\\crt\\mqtt" FILES ${AWS_CRT_MQTT_HEADERS}) source_group("Header Files\\aws\\crt\\http" FILES ${AWS_CRT_HTTP_HEADERS}) source_group("Header Files\\aws\\crt\\endpoints" FILES ${AWS_CRT_ENDPOINT_HEADERS}) + source_group("Header Files\\aws\\crt\\cbor" FILES ${AWS_CRT_CBOR_HEADERS}) source_group("Source Files" FILES ${AWS_CRT_SRC}) source_group("Source Files\\auth" FILES ${AWS_CRT_AUTH_SRC}) @@ -263,6 +265,7 @@ if(WIN32) source_group("Source Files\\mqtt" FILES ${AWS_CRT_MQTT_SRC}) source_group("Source Files\\http" FILES ${AWS_CRT_HTTP_SRC}) source_group("Source Files\\endpoints" FILES ${AWS_CRT_ENDPOINTS_SRC}) + source_group("Source Files\\cbor" FILES ${AWS_CRT_CBOR_SRC}) endif() endif() @@ -332,6 +335,7 @@ install(FILES ${AWS_CRT_IOT_HEADERS} DESTINATION "include/aws/iot" COMPONENT Dev install(FILES ${AWS_CRT_MQTT_HEADERS} DESTINATION "include/aws/crt/mqtt" COMPONENT Development) install(FILES ${AWS_CRT_HTTP_HEADERS} DESTINATION "include/aws/crt/http" COMPONENT Development) install(FILES ${AWS_CRT_ENDPOINT_HEADERS} DESTINATION "include/aws/crt/endpoints" COMPONENT Development) +install(FILES ${AWS_CRT_CBOR_HEADERS} DESTINATION "include/aws/crt/cbor" COMPONENT Development) install( TARGETS ${PROJECT_NAME} diff --git a/crt/aws-c-common b/crt/aws-c-common index ae7b067d9..7210cf204 160000 --- a/crt/aws-c-common +++ b/crt/aws-c-common @@ -1 +1 @@ -Subproject commit ae7b067d9274d2d3faa1d3ae42d489a6986661f7 +Subproject commit 7210cf20440182d10b8cf58d876f1ab7dfe8d165 diff --git a/include/aws/crt/cbor/Cbor.h b/include/aws/crt/cbor/Cbor.h new file mode 100644 index 000000000..dcaab7ee0 --- /dev/null +++ b/include/aws/crt/cbor/Cbor.h @@ -0,0 +1,364 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include +#include + +namespace Aws +{ + namespace Crt + { + namespace Cbor + { + /** + * The types used by APIs, not 1:1 with major types. + * It's an extension for CBOR major type in RFC8949 section 3.1. + * Major type 0 - Uint + * Major type 1 - NegInt + * Major type 2 - Bytes / IndefBytesStart + * Major type 3 - Text / IndefTextStart + * Major type 4 - ArrayStart / IndefArrayStart + * Major type 5 - MapStart / IndefMapStart + * Major type 6 - Tag + * Major type 7: + * - 20/21 - Bool + * - 22 - Null + * - 23 - Undefined + * - 25/26/27 - Float + * - 31 - Break + * - Rest of the values are not supported. + */ + enum class CborType + { + Unknown = AWS_CBOR_TYPE_UNKNOWN, + Uint = AWS_CBOR_TYPE_UINT, + NegInt = AWS_CBOR_TYPE_NEGINT, + Float = AWS_CBOR_TYPE_FLOAT, + Bytes = AWS_CBOR_TYPE_BYTES, + Text = AWS_CBOR_TYPE_TEXT, + ArrayStart = AWS_CBOR_TYPE_ARRAY_START, + MapStart = AWS_CBOR_TYPE_MAP_START, + Tag = AWS_CBOR_TYPE_TAG, + Bool = AWS_CBOR_TYPE_BOOL, + Null = AWS_CBOR_TYPE_NULL, + Undefined = AWS_CBOR_TYPE_UNDEFINED, + Break = AWS_CBOR_TYPE_BREAK, + IndefBytesStart = AWS_CBOR_TYPE_INDEF_BYTES_START, + IndefTextStart = AWS_CBOR_TYPE_INDEF_TEXT_START, + IndefArrayStart = AWS_CBOR_TYPE_INDEF_ARRAY_START, + IndefMapStart = AWS_CBOR_TYPE_INDEF_MAP_START, + }; + + class AWS_CRT_CPP_API CborEncoder + { + public: + CborEncoder(const CborEncoder &) = delete; + CborEncoder(CborEncoder &&) = delete; + CborEncoder &operator=(const CborEncoder &) = delete; + CborEncoder &operator=(CborEncoder &&) = delete; + + CborEncoder(Allocator *allocator) noexcept; + ~CborEncoder() noexcept; + + /** + * 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. + * + * @return the current encoded data + */ + ByteCursor GetEncodedData() noexcept; + + /** + * Clear the current encoded buffer from encoder. + */ + void Reset() noexcept; + + /** + * Encode a AWS_CBOR_TYPE_UINT value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1 + * + * @param value value to encode. + */ + void WriteUint(uint64_t value) noexcept; + + /** + * 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 value value to encode, which is (-1 - represented value) + */ + void WriteNegInt(uint64_t value) noexcept; + + /** + * 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 conversion will not cause precision loss. + * + * @param value value to encode. + */ + void WriteFloat(double value) noexcept; + + /** + * Encode a Bytes value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1, the length of "value" will be encoded first and then the value of + * "value" will be followed. + * + * @param value value to encode. + */ + void WriteBytes(ByteCursor value) noexcept; + + /** + * Encode a Text value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1, the length of "value" will be encoded first and then the value of + * "value" will be followed. + * + * @param value value to encode. + */ + void WriteText(ByteCursor value) noexcept; + + /** + * Encode an ArrayStart value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1 + * Notes: it's user's responsibility to keep the integrity of the array to be encoded. + * + * @param number_entries the number of CBOR data items to be followed as the content of the array. + */ + void WriteArrayStart(size_t number_entries) noexcept; + + /** + * Encode a MapStart value to "smallest possible" in encoder's buffer. + * Referring to RFC8949 section 4.2.1 + * + * Notes: it's user's responsibility to keep the integrity of the map to be encoded. + * + * @param number_entries the number of pair of CBOR data items as key and value to be followed as + * the content of the map. + */ + void WriteMapStart(size_t number_entries) noexcept; + + /** + * Encode a 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 tag_number The tag value to encode. + */ + void WriteTag(uint64_t tag_number) noexcept; + + /** + * Encode a simple value Null + */ + void WriteNull() noexcept; + + /** + * Encode a simple value Undefined + */ + void WriteUndefined() noexcept; + + /** + * Encode a simple value Bool + */ + void WriteBool(bool value) noexcept; + + /** + * Encode a simple value Break + * Notes: no error checking, it's user's responsibility to track the break to close the corresponding + * indef_start + */ + void WriteBreak() noexcept; + + /** + * Encode an IndefBytesStart + * Notes: no error checking, it's user's responsibility to add corresponding data and the break to close + * the indef_start + */ + void WriteIndefBytesStart() noexcept; + + /** + * Encode an IndefTextStart + * Notes: no error checking, it's user's responsibility to add corresponding data and the break to close + * the indef_start + */ + void WriteIndefTextStart() noexcept; + + /** + * Encode an IndefArrayStart + * Notes: no error checking, it's user's responsibility to add corresponding data and the break to close + * the indef_start + */ + void WriteIndefArrayStart() noexcept; + + /** + * Encode an IndefMapStart + * Notes: no error checking, it's user's responsibility to add corresponding data and the break to close + * the indef_start + */ + void WriteIndefMapStart() noexcept; + + private: + struct aws_cbor_encoder *m_encoder; + }; + + class AWS_CRT_CPP_API CborDecoder + { + + public: + CborDecoder(const CborDecoder &) = delete; + CborDecoder(CborDecoder &&) = delete; + CborDecoder &operator=(const CborDecoder &) = delete; + CborDecoder &operator=(CborDecoder &&) = delete; + + /** + * Construct a new Cbor Decoder object + * + * @param allocator + * @param src The src data to decode from. + */ + CborDecoder(Allocator *allocator, ByteCursor src) noexcept; + ~CborDecoder() noexcept; + + /** + * 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. + * + * @return The length of bytes remaining of the decoder source. + */ + size_t GetRemainingLength() noexcept; + + /** + * Decode the next element and store it in the decoder cache if there was no element cached. + * If there was an element cached, just return the type of the cached element. + * + * @param out_type + * @return success/failure + */ + bool PeekType(CborType &out_type) noexcept; + + /** + * 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. + * + * @return success/failure + */ + bool ConsumeNextWholeDataItem() noexcept; + + /** + * 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" + * + * @return success/failure + */ + bool ConsumeNextSingleElement() noexcept; + + /** + * Get the next element based on the type. If the next element doesn't match the expected type, an error + * will be raised. If the next element has already been cached, it will consume the cached item when no + * error was returned. Specifically: + * - Uint - PopNextUnsignedIntVal + * - NegInt - PopNextNegativeIntVal, it represents (-1 - &out) + * - Float - PopNextFloatVal + * - Bytes - PopNextBytesVal + * - Text - PopNextTextVal + * + * @param out + * @return success/failure + */ + bool PopNextUnsignedIntVal(uint64_t &out) noexcept; + bool PopNextNegativeIntVal(uint64_t &out) noexcept; + bool PopNextFloatVal(double &out) noexcept; + bool PopNextBooleanVal(bool &out) noexcept; + bool PopNextBytesVal(ByteCursor &out) noexcept; + bool PopNextTextVal(ByteCursor &out) noexcept; + + /** + * Get the next ArrayStart element. Only consume the ArrayStart 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 IndefArrayStart from PeekType + * - Call ConsumeNextSingleElement to pop the indefinite-length start. + * - Decode the next data item until Break is read. + * + * @param out_size Store the size of array if succeeded. + * @return success/failure + */ + bool PopNextArrayStart(uint64_t &out_size) noexcept; + + /** + * Get the next MapStart element. Only consume the MapStart 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 IndefMapStart from PeekType + * - Call ConsumeNextSingleElement to pop the indefinite-length start. + * - Decode the next data item until Break is read. + * + * @param out_size Store the size of map if succeeded. + * @return success/failure + */ + bool PopNextMapStart(uint64_t &out_size) noexcept; + + /** + * Get the next Tag element. Only consume the Tag element and set the tag value to out_tag_val, + * not the content of the tagged value. The next CBOR data item will be the content of the tagged value + * for a valid CBOR data. + * + * @param out_tag_val Store the tag value if succeeded. + * @return success/failure + */ + bool PopNextTagVal(uint64_t &out_tag_val) noexcept; + + /** + * @return the value of the last aws error encountered by operations on this instance. + */ + int LastError() const noexcept { return m_lastError ? m_lastError : AWS_ERROR_UNKNOWN; } + + private: + Allocator *m_allocator; + struct aws_cbor_decoder *m_decoder; + /* Error */ + int m_lastError; + }; + } // namespace Cbor + + } // namespace Crt +} // namespace Aws diff --git a/source/cbor/Cbor.cpp b/source/cbor/Cbor.cpp new file mode 100644 index 000000000..50f5ee12c --- /dev/null +++ b/source/cbor/Cbor.cpp @@ -0,0 +1,209 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include +#include + +namespace Aws +{ + namespace Crt + { + namespace Cbor + { + /***************************************************** + * + * CborEncoder + * + *****************************************************/ + CborEncoder::CborEncoder(Crt::Allocator *allocator) noexcept + { + m_encoder = aws_cbor_encoder_new(allocator); + } + + CborEncoder::~CborEncoder() noexcept { aws_cbor_encoder_destroy(m_encoder); } + + ByteCursor CborEncoder::GetEncodedData() noexcept { return aws_cbor_encoder_get_encoded_data(m_encoder); } + void CborEncoder::Reset() noexcept { aws_cbor_encoder_reset(m_encoder); } + + void CborEncoder::WriteUint(uint64_t value) noexcept { aws_cbor_encoder_write_uint(m_encoder, value); } + void CborEncoder::WriteNegInt(uint64_t value) noexcept { aws_cbor_encoder_write_negint(m_encoder, value); } + + void CborEncoder::WriteFloat(double value) noexcept { aws_cbor_encoder_write_float(m_encoder, value); } + + void CborEncoder::WriteBytes(ByteCursor value) noexcept { aws_cbor_encoder_write_bytes(m_encoder, value); } + + void CborEncoder::WriteText(ByteCursor value) noexcept { aws_cbor_encoder_write_text(m_encoder, value); } + + void CborEncoder::WriteArrayStart(size_t number_entries) noexcept + { + aws_cbor_encoder_write_array_start(m_encoder, number_entries); + } + + void CborEncoder::WriteMapStart(size_t number_entries) noexcept + { + aws_cbor_encoder_write_map_start(m_encoder, number_entries); + } + + void CborEncoder::WriteTag(uint64_t tag_number) noexcept + { + aws_cbor_encoder_write_tag(m_encoder, tag_number); + } + + void CborEncoder::WriteNull() noexcept { aws_cbor_encoder_write_null(m_encoder); } + + void CborEncoder::WriteUndefined() noexcept { aws_cbor_encoder_write_undefined(m_encoder); } + + void CborEncoder::WriteBool(bool value) noexcept { aws_cbor_encoder_write_bool(m_encoder, value); } + + void CborEncoder::WriteBreak() noexcept { aws_cbor_encoder_write_break(m_encoder); } + + void CborEncoder::WriteIndefBytesStart() noexcept { aws_cbor_encoder_write_indef_bytes_start(m_encoder); } + + void CborEncoder::WriteIndefTextStart() noexcept { aws_cbor_encoder_write_indef_text_start(m_encoder); } + + void CborEncoder::WriteIndefArrayStart() noexcept { aws_cbor_encoder_write_indef_array_start(m_encoder); } + + void CborEncoder::WriteIndefMapStart() noexcept { aws_cbor_encoder_write_indef_map_start(m_encoder); } + + /***************************************************** + * + * CborDecoder + * + *****************************************************/ + CborDecoder::CborDecoder(Crt::Allocator *allocator, ByteCursor src) noexcept + { + m_decoder = aws_cbor_decoder_new(allocator, src); + } + + CborDecoder::~CborDecoder() noexcept { aws_cbor_decoder_destroy(m_decoder); } + + size_t CborDecoder::GetRemainingLength() noexcept + { + return aws_cbor_decoder_get_remaining_length(m_decoder); + } + + bool CborDecoder::PeekType(CborType &out_type) noexcept + { + enum aws_cbor_type out_type_c = AWS_CBOR_TYPE_UNKNOWN; + if (aws_cbor_decoder_peek_type(m_decoder, &out_type_c) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + out_type = (CborType)out_type_c; + return true; + } + bool CborDecoder::ConsumeNextWholeDataItem() noexcept + { + if (aws_cbor_decoder_consume_next_whole_data_item(m_decoder) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::ConsumeNextSingleElement() noexcept + { + if (aws_cbor_decoder_consume_next_single_element(m_decoder) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextUnsignedIntVal(uint64_t &out) noexcept + { + if (aws_cbor_decoder_pop_next_unsigned_int_val(m_decoder, &out) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextNegativeIntVal(uint64_t &out) noexcept + { + if (aws_cbor_decoder_pop_next_negative_int_val(m_decoder, &out) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextFloatVal(double &out) noexcept + { + if (aws_cbor_decoder_pop_next_float_val(m_decoder, &out) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextBooleanVal(bool &out) noexcept + { + if (aws_cbor_decoder_pop_next_boolean_val(m_decoder, &out) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextBytesVal(ByteCursor &out) noexcept + { + if (aws_cbor_decoder_pop_next_bytes_val(m_decoder, &out) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextTextVal(ByteCursor &out) noexcept + { + if (aws_cbor_decoder_pop_next_text_val(m_decoder, &out) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextArrayStart(uint64_t &out_size) noexcept + { + if (aws_cbor_decoder_pop_next_array_start(m_decoder, &out_size) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextMapStart(uint64_t &out_size) noexcept + { + if (aws_cbor_decoder_pop_next_map_start(m_decoder, &out_size) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + bool CborDecoder::PopNextTagVal(uint64_t &out_tag_val) noexcept + { + if (aws_cbor_decoder_pop_next_tag_val(m_decoder, &out_tag_val) != AWS_OP_SUCCESS) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + } // namespace Cbor + } // namespace Crt +} // namespace Aws diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d0576813b..e78c7d974 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -274,6 +274,8 @@ if(AWS_HAS_CI_ENVIRONMENT AND NOT BYO_CRYPTO) endif() endif() +add_test_case(CborSanityTest) + generate_cpp_test_driver(${TEST_BINARY_NAME}) aws_add_sanitizers(${TEST_BINARY_NAME}) diff --git a/tests/CborTest.cpp b/tests/CborTest.cpp new file mode 100644 index 000000000..f85659d40 --- /dev/null +++ b/tests/CborTest.cpp @@ -0,0 +1,36 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include +#include +#include + +using namespace Aws::Crt; + +static int s_CborSanityTest(struct aws_allocator *allocator, void *ctx) +{ + (void)ctx; + { + ApiHandle apiHandle(allocator); + Cbor::CborEncoder encoder(allocator); + uint64_t expected_val = 100; + encoder.WriteUint(expected_val); + ByteCursor cursor = encoder.GetEncodedData(); + + Cbor::CborDecoder decoder(allocator, cursor); + Cbor::CborType out_type = Cbor::CborType::Unknown; + ASSERT_TRUE(decoder.PeekType(out_type)); + ASSERT_UINT_EQUALS((int)out_type, (int)Cbor::CborType::Uint); + uint64_t out_val = 0; + ASSERT_TRUE(decoder.PopNextUnsignedIntVal(out_val)); + ASSERT_UINT_EQUALS(expected_val, out_val); + + auto length = decoder.GetRemainingLength(); + ASSERT_UINT_EQUALS(0, length); + } + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(CborSanityTest, s_CborSanityTest) From 798e0b0a05c1705da60a297db729ec17e2deef68 Mon Sep 17 00:00:00 2001 From: Dengke Date: Wed, 12 Jun 2024 16:19:01 -0700 Subject: [PATCH 2/9] unused field --- include/aws/crt/cbor/Cbor.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/aws/crt/cbor/Cbor.h b/include/aws/crt/cbor/Cbor.h index dcaab7ee0..8c0b2001a 100644 --- a/include/aws/crt/cbor/Cbor.h +++ b/include/aws/crt/cbor/Cbor.h @@ -353,7 +353,6 @@ namespace Aws int LastError() const noexcept { return m_lastError ? m_lastError : AWS_ERROR_UNKNOWN; } private: - Allocator *m_allocator; struct aws_cbor_decoder *m_decoder; /* Error */ int m_lastError; From 82ca64e8506a10a7e70f9ca775b157892ce0f560 Mon Sep 17 00:00:00 2001 From: Dengke Date: Thu, 13 Jun 2024 14:45:50 -0700 Subject: [PATCH 3/9] alright, add test for basically every method --- tests/CborTest.cpp | 112 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 7 deletions(-) diff --git a/tests/CborTest.cpp b/tests/CborTest.cpp index f85659d40..427a70a00 100644 --- a/tests/CborTest.cpp +++ b/tests/CborTest.cpp @@ -10,21 +10,119 @@ using namespace Aws::Crt; static int s_CborSanityTest(struct aws_allocator *allocator, void *ctx) { + /** + * Simply test every method works. + */ (void)ctx; { ApiHandle apiHandle(allocator); Cbor::CborEncoder encoder(allocator); - uint64_t expected_val = 100; - encoder.WriteUint(expected_val); + + // Define expected values + auto expected_uint_val = 42ULL; + auto expected_negint_val = 123ULL; + auto expected_float_val = 3.14; + ByteCursor expected_bytes_val = aws_byte_cursor_from_c_str("write more"); + ByteCursor expected_text_val = aws_byte_cursor_from_c_str("test"); + size_t expected_array_size = 5; + size_t expected_map_size = 3; + auto expected_tag_val = 999ULL; + bool expected_bool_val = true; + + encoder.WriteUint(expected_uint_val); + encoder.WriteNegInt(expected_negint_val); + encoder.WriteFloat(expected_float_val); + encoder.WriteBytes(expected_bytes_val); + encoder.WriteText(expected_text_val); + encoder.WriteArrayStart(expected_array_size); + encoder.WriteMapStart(expected_map_size); + encoder.WriteTag(expected_tag_val); + encoder.WriteBool(expected_bool_val); + encoder.WriteNull(); + encoder.WriteUndefined(); + encoder.WriteBreak(); + encoder.WriteIndefBytesStart(); + encoder.WriteIndefTextStart(); + encoder.WriteIndefArrayStart(); + encoder.WriteIndefMapStart(); + ByteCursor cursor = encoder.GetEncodedData(); Cbor::CborDecoder decoder(allocator, cursor); Cbor::CborType out_type = Cbor::CborType::Unknown; - ASSERT_TRUE(decoder.PeekType(out_type)); - ASSERT_UINT_EQUALS((int)out_type, (int)Cbor::CborType::Uint); - uint64_t out_val = 0; - ASSERT_TRUE(decoder.PopNextUnsignedIntVal(out_val)); - ASSERT_UINT_EQUALS(expected_val, out_val); + + // Check for Uint + uint64_t uint_val; + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Uint); + ASSERT_TRUE(decoder.PopNextUnsignedIntVal(uint_val) && uint_val == expected_uint_val); + + // Check for NegInt + uint64_t negint_val; + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::NegInt); + ASSERT_TRUE(decoder.PopNextNegativeIntVal(negint_val) && negint_val == expected_negint_val); + + // Check for Float + double float_val; + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Float); + ASSERT_TRUE(decoder.PopNextFloatVal(float_val) && float_val == expected_float_val); + + // Check for Bytes + ByteCursor bytes_val; + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Bytes); + ASSERT_TRUE(decoder.PopNextBytesVal(bytes_val) && aws_byte_cursor_eq(&bytes_val, &expected_bytes_val)); + + // Check for Text + ByteCursor text_val; + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Text); + ASSERT_TRUE(decoder.PopNextTextVal(text_val) && aws_byte_cursor_eq(&text_val, &expected_text_val)); + + // Check for ArrayStart + uint64_t array_size; + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::ArrayStart); + ASSERT_TRUE(decoder.PopNextArrayStart(array_size) && array_size == expected_array_size); + + // Check for MapStart + uint64_t map_size; + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::MapStart); + ASSERT_TRUE(decoder.PopNextMapStart(map_size) && map_size == expected_map_size); + + // Check for Tag + uint64_t tag_val; + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Tag); + ASSERT_TRUE(decoder.PopNextTagVal(tag_val) && tag_val == expected_tag_val); + + // Check for Bool + bool bool_val; + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Bool); + ASSERT_TRUE(decoder.PopNextBooleanVal(bool_val) && bool_val == expected_bool_val); + + // Check for Null + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Null); + ASSERT_TRUE(decoder.ConsumeNextWholeDataItem()); + + // Check for Undefined + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Undefined); + ASSERT_TRUE(decoder.ConsumeNextWholeDataItem()); + + // Check for Break + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Break); + ASSERT_TRUE(decoder.ConsumeNextSingleElement()); + + // Check for IndefBytesStart + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::IndefBytesStart); + ASSERT_TRUE(decoder.ConsumeNextSingleElement()); + + // Check for IndefTextStart + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::IndefTextStart); + ASSERT_TRUE(decoder.ConsumeNextSingleElement()); + + // Check for IndefArrayStart + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::IndefArrayStart); + ASSERT_TRUE(decoder.ConsumeNextSingleElement()); + + // Check for IndefMapStart + ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::IndefMapStart); + ASSERT_TRUE(decoder.ConsumeNextSingleElement()); auto length = decoder.GetRemainingLength(); ASSERT_UINT_EQUALS(0, length); From c047ebc96d38ab9dc173ec3241e99066b4e742e2 Mon Sep 17 00:00:00 2001 From: Dengke Date: Thu, 13 Jun 2024 15:14:35 -0700 Subject: [PATCH 4/9] Just need one extra header file --- include/aws/crt/cbor/Cbor.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/aws/crt/cbor/Cbor.h b/include/aws/crt/cbor/Cbor.h index 8c0b2001a..eff62d0a7 100644 --- a/include/aws/crt/cbor/Cbor.h +++ b/include/aws/crt/cbor/Cbor.h @@ -6,7 +6,6 @@ #include -#include #include namespace Aws From ffd06248627c6be987bfc30341566b3324b526ef Mon Sep 17 00:00:00 2001 From: Dengke Date: Mon, 17 Jun 2024 15:56:02 -0700 Subject: [PATCH 5/9] comments and add a timestamp test as an example --- include/aws/crt/cbor/Cbor.h | 4 +- source/cbor/Cbor.cpp | 87 ++++++++++++++++++++------ tests/CMakeLists.txt | 1 + tests/CborTest.cpp | 121 ++++++++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+), 20 deletions(-) diff --git a/include/aws/crt/cbor/Cbor.h b/include/aws/crt/cbor/Cbor.h index eff62d0a7..d082ff978 100644 --- a/include/aws/crt/cbor/Cbor.h +++ b/include/aws/crt/cbor/Cbor.h @@ -53,7 +53,7 @@ namespace Aws IndefMapStart = AWS_CBOR_TYPE_INDEF_MAP_START, }; - class AWS_CRT_CPP_API CborEncoder + class AWS_CRT_CPP_API CborEncoder final { public: CborEncoder(const CborEncoder &) = delete; @@ -208,7 +208,7 @@ namespace Aws struct aws_cbor_encoder *m_encoder; }; - class AWS_CRT_CPP_API CborDecoder + class AWS_CRT_CPP_API CborDecoder final { public: diff --git a/source/cbor/Cbor.cpp b/source/cbor/Cbor.cpp index 50f5ee12c..e505dcb73 100644 --- a/source/cbor/Cbor.cpp +++ b/source/cbor/Cbor.cpp @@ -21,19 +21,43 @@ namespace Aws m_encoder = aws_cbor_encoder_new(allocator); } - CborEncoder::~CborEncoder() noexcept { aws_cbor_encoder_destroy(m_encoder); } + CborEncoder::~CborEncoder() noexcept + { + aws_cbor_encoder_destroy(m_encoder); + } - ByteCursor CborEncoder::GetEncodedData() noexcept { return aws_cbor_encoder_get_encoded_data(m_encoder); } - void CborEncoder::Reset() noexcept { aws_cbor_encoder_reset(m_encoder); } + ByteCursor CborEncoder::GetEncodedData() noexcept + { + return aws_cbor_encoder_get_encoded_data(m_encoder); + } + void CborEncoder::Reset() noexcept + { + aws_cbor_encoder_reset(m_encoder); + } - void CborEncoder::WriteUint(uint64_t value) noexcept { aws_cbor_encoder_write_uint(m_encoder, value); } - void CborEncoder::WriteNegInt(uint64_t value) noexcept { aws_cbor_encoder_write_negint(m_encoder, value); } + void CborEncoder::WriteUint(uint64_t value) noexcept + { + aws_cbor_encoder_write_uint(m_encoder, value); + } + void CborEncoder::WriteNegInt(uint64_t value) noexcept + { + aws_cbor_encoder_write_negint(m_encoder, value); + } - void CborEncoder::WriteFloat(double value) noexcept { aws_cbor_encoder_write_float(m_encoder, value); } + void CborEncoder::WriteFloat(double value) noexcept + { + aws_cbor_encoder_write_float(m_encoder, value); + } - void CborEncoder::WriteBytes(ByteCursor value) noexcept { aws_cbor_encoder_write_bytes(m_encoder, value); } + void CborEncoder::WriteBytes(ByteCursor value) noexcept + { + aws_cbor_encoder_write_bytes(m_encoder, value); + } - void CborEncoder::WriteText(ByteCursor value) noexcept { aws_cbor_encoder_write_text(m_encoder, value); } + void CborEncoder::WriteText(ByteCursor value) noexcept + { + aws_cbor_encoder_write_text(m_encoder, value); + } void CborEncoder::WriteArrayStart(size_t number_entries) noexcept { @@ -50,21 +74,45 @@ namespace Aws aws_cbor_encoder_write_tag(m_encoder, tag_number); } - void CborEncoder::WriteNull() noexcept { aws_cbor_encoder_write_null(m_encoder); } + void CborEncoder::WriteNull() noexcept + { + aws_cbor_encoder_write_null(m_encoder); + } - void CborEncoder::WriteUndefined() noexcept { aws_cbor_encoder_write_undefined(m_encoder); } + void CborEncoder::WriteUndefined() noexcept + { + aws_cbor_encoder_write_undefined(m_encoder); + } - void CborEncoder::WriteBool(bool value) noexcept { aws_cbor_encoder_write_bool(m_encoder, value); } + void CborEncoder::WriteBool(bool value) noexcept + { + aws_cbor_encoder_write_bool(m_encoder, value); + } - void CborEncoder::WriteBreak() noexcept { aws_cbor_encoder_write_break(m_encoder); } + void CborEncoder::WriteBreak() noexcept + { + aws_cbor_encoder_write_break(m_encoder); + } - void CborEncoder::WriteIndefBytesStart() noexcept { aws_cbor_encoder_write_indef_bytes_start(m_encoder); } + void CborEncoder::WriteIndefBytesStart() noexcept + { + aws_cbor_encoder_write_indef_bytes_start(m_encoder); + } - void CborEncoder::WriteIndefTextStart() noexcept { aws_cbor_encoder_write_indef_text_start(m_encoder); } + void CborEncoder::WriteIndefTextStart() noexcept + { + aws_cbor_encoder_write_indef_text_start(m_encoder); + } - void CborEncoder::WriteIndefArrayStart() noexcept { aws_cbor_encoder_write_indef_array_start(m_encoder); } + void CborEncoder::WriteIndefArrayStart() noexcept + { + aws_cbor_encoder_write_indef_array_start(m_encoder); + } - void CborEncoder::WriteIndefMapStart() noexcept { aws_cbor_encoder_write_indef_map_start(m_encoder); } + void CborEncoder::WriteIndefMapStart() noexcept + { + aws_cbor_encoder_write_indef_map_start(m_encoder); + } /***************************************************** * @@ -76,7 +124,10 @@ namespace Aws m_decoder = aws_cbor_decoder_new(allocator, src); } - CborDecoder::~CborDecoder() noexcept { aws_cbor_decoder_destroy(m_decoder); } + CborDecoder::~CborDecoder() noexcept + { + aws_cbor_decoder_destroy(m_decoder); + } size_t CborDecoder::GetRemainingLength() noexcept { @@ -205,5 +256,5 @@ namespace Aws } } // namespace Cbor - } // namespace Crt + } // namespace Crt } // namespace Aws diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e78c7d974..dfdc36083 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -275,6 +275,7 @@ if(AWS_HAS_CI_ENVIRONMENT AND NOT BYO_CRYPTO) endif() add_test_case(CborSanityTest) +add_test_case(CborTimeStampTest) generate_cpp_test_driver(${TEST_BINARY_NAME}) diff --git a/tests/CborTest.cpp b/tests/CborTest.cpp index 427a70a00..1f1e0b229 100644 --- a/tests/CborTest.cpp +++ b/tests/CborTest.cpp @@ -132,3 +132,124 @@ static int s_CborSanityTest(struct aws_allocator *allocator, void *ctx) } AWS_TEST_CASE(CborSanityTest, s_CborSanityTest) + +static void s_encode_timestamp_helper( + Cbor::CborEncoder &encoder, + const std::chrono::system_clock::time_point &timePoint) noexcept +{ + // Get the duration since epoch + auto duration = timePoint.time_since_epoch(); + // Convert the duration to seconds (using double for potential fractional seconds) + double seconds = std::chrono::duration(duration).count(); + encoder.WriteTag(AWS_CBOR_TAG_EPOCH_TIME); + // Use the encoder to write the duration in seconds + encoder.WriteFloat(seconds); +} + +static int s_decode_timestamp_helper(Cbor::CborDecoder &decoder, std::chrono::system_clock::time_point &outTimePoint) +{ + Cbor::CborType out_type = Cbor::CborType::Unknown; + if (!decoder.PeekType(out_type) && out_type != Cbor::CborType::Tag) + { + return AWS_OP_ERR; + } + uint64_t tag_val = 0; + if (!decoder.PopNextTagVal(tag_val) && tag_val != AWS_CBOR_TAG_EPOCH_TIME) + { + return AWS_OP_ERR; + } + if (decoder.PeekType(out_type)) + { + switch (out_type) + { + case Cbor::CborType::Uint: + case Cbor::CborType::NegInt: + { + int64_t secs_timestamp = 0; + uint64_t unsigned_val = 0; + if (out_type == Cbor::CborType::Uint) + { + decoder.PopNextUnsignedIntVal(unsigned_val); + if (unsigned_val > INT64_MAX) + { + /* Overflow */ + return AWS_OP_ERR; + } + secs_timestamp = (int64_t)unsigned_val; + } + else + { + decoder.PopNextNegativeIntVal(unsigned_val); + if (unsigned_val > INT64_MAX) + { + /* Overflow */ + return AWS_OP_ERR; + } + /* convert the encoded number to real negative number */ + secs_timestamp = (int64_t)(-1 - unsigned_val); + } + std::chrono::duration timestamp(secs_timestamp); + outTimePoint = std::chrono::system_clock::time_point(timestamp); + return AWS_OP_SUCCESS; + } + case Cbor::CborType::Float: + { + double double_val = 0; + decoder.PopNextFloatVal(double_val); + std::chrono::duration timestamp(double_val); + outTimePoint = std::chrono::system_clock::time_point( + std::chrono::duration_cast(timestamp)); + return AWS_OP_SUCCESS; + } + default: + break; + } + } + return AWS_OP_ERR; +} + +static bool s_check_time_point_equals_ms_precision( + const std::chrono::time_point &a, + const std::chrono::time_point &b) +{ + using Ms = std::chrono::milliseconds; + auto a_ms = std::chrono::duration_cast(a.time_since_epoch()); + auto b_ms = std::chrono::duration_cast(b.time_since_epoch()); + if (a_ms == b_ms) + { + return true; + } + else + { + return false; + } +} + +static int s_CborTimeStampTest(struct aws_allocator *allocator, void *ctx) +{ + /** + * Simply test every method works. + */ + (void)ctx; + { + ApiHandle apiHandle(allocator); + Cbor::CborEncoder encoder(allocator); + const std::chrono::time_point now = std::chrono::system_clock::now(); + + s_encode_timestamp_helper(encoder, now); + ByteCursor cursor = encoder.GetEncodedData(); + + Cbor::CborDecoder decoder(allocator, cursor); + std::chrono::time_point decoded; + ASSERT_SUCCESS(s_decode_timestamp_helper(decoder, decoded)); + + /* We only check the ms precision */ + ASSERT_TRUE(s_check_time_point_equals_ms_precision(decoded, now)); + auto length = decoder.GetRemainingLength(); + ASSERT_UINT_EQUALS(0, length); + } + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(CborTimeStampTest, s_CborTimeStampTest) From 1bf490bde78a686e25e4d02df57a31ed28b95f10 Mon Sep 17 00:00:00 2001 From: Dengke Date: Mon, 17 Jun 2024 16:08:28 -0700 Subject: [PATCH 6/9] trivial --- tests/CborTest.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/CborTest.cpp b/tests/CborTest.cpp index 1f1e0b229..80a979914 100644 --- a/tests/CborTest.cpp +++ b/tests/CborTest.cpp @@ -137,10 +137,10 @@ static void s_encode_timestamp_helper( Cbor::CborEncoder &encoder, const std::chrono::system_clock::time_point &timePoint) noexcept { - // Get the duration since epoch - auto duration = timePoint.time_since_epoch(); - // Convert the duration to seconds (using double for potential fractional seconds) - double seconds = std::chrono::duration(duration).count(); + /* Get seconds with MS precision. */ + std::chrono::duration timestamp(timePoint.time_since_epoch()); + double seconds = timestamp.count(); + encoder.WriteTag(AWS_CBOR_TAG_EPOCH_TIME); // Use the encoder to write the duration in seconds encoder.WriteFloat(seconds); @@ -228,7 +228,7 @@ static bool s_check_time_point_equals_ms_precision( static int s_CborTimeStampTest(struct aws_allocator *allocator, void *ctx) { /** - * Simply test every method works. + * Example of how timestamp will be encoded and decoded with `std::chrono::system_clock::time_point` */ (void)ctx; { From 9caf9f31df09daa4ef49f4d48b21f84317877faa Mon Sep 17 00:00:00 2001 From: Dengke Date: Thu, 20 Jun 2024 14:30:47 -0700 Subject: [PATCH 7/9] comments addressed --- CMakeLists.txt | 1 - include/aws/crt/cbor/Cbor.h | 65 ++++++++-------- source/cbor/Cbor.cpp | 74 ++++++++++-------- tests/CborTest.cpp | 149 ++++++++++++++++-------------------- 4 files changed, 144 insertions(+), 145 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7623aed10..fb54a14e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,7 +194,6 @@ endif() file(GLOB AWS_CRT_CPP_HEADERS ${AWS_CRT_PUBLIC_HEADERS} - ${AWS_CRT_EXTERNAL_HEADERS} ) file(GLOB AWS_CRT_SRC diff --git a/include/aws/crt/cbor/Cbor.h b/include/aws/crt/cbor/Cbor.h index d082ff978..3cc120cb7 100644 --- a/include/aws/crt/cbor/Cbor.h +++ b/include/aws/crt/cbor/Cbor.h @@ -17,7 +17,7 @@ namespace Aws /** * The types used by APIs, not 1:1 with major types. * It's an extension for CBOR major type in RFC8949 section 3.1. - * Major type 0 - Uint + * Major type 0 - UInt * Major type 1 - NegInt * Major type 2 - Bytes / IndefBytesStart * Major type 3 - Text / IndefTextStart @@ -35,7 +35,7 @@ namespace Aws enum class CborType { Unknown = AWS_CBOR_TYPE_UNKNOWN, - Uint = AWS_CBOR_TYPE_UINT, + UInt = AWS_CBOR_TYPE_UINT, NegInt = AWS_CBOR_TYPE_NEGINT, Float = AWS_CBOR_TYPE_FLOAT, Bytes = AWS_CBOR_TYPE_BYTES, @@ -61,7 +61,7 @@ namespace Aws CborEncoder &operator=(const CborEncoder &) = delete; CborEncoder &operator=(CborEncoder &&) = delete; - CborEncoder(Allocator *allocator) noexcept; + CborEncoder(Allocator *allocator = ApiAllocator()) noexcept; ~CborEncoder() noexcept; /** @@ -83,7 +83,7 @@ namespace Aws * * @param value value to encode. */ - void WriteUint(uint64_t value) noexcept; + void WriteUInt(uint64_t value) noexcept; /** * Encode a AWS_CBOR_TYPE_NEGINT value to "smallest possible" in encoder's buffer. @@ -223,7 +223,7 @@ namespace Aws * @param allocator * @param src The src data to decode from. */ - CborDecoder(Allocator *allocator, ByteCursor src) noexcept; + CborDecoder(ByteCursor src, Allocator *allocator = ApiAllocator()) noexcept; ~CborDecoder() noexcept; /** @@ -238,10 +238,11 @@ namespace Aws * Decode the next element and store it in the decoder cache if there was no element cached. * If there was an element cached, just return the type of the cached element. * - * @param out_type - * @return success/failure + * @return If successful, return the type of next element + * If not, return will be none and Aws::Crt::LastError() can be + * used to retrieve CRT error code. */ - bool PeekType(CborType &out_type) noexcept; + Optional PeekType() noexcept; /** * Consume the next data item, includes all the content within the data item. @@ -249,7 +250,7 @@ namespace Aws * 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 + * BF -- Start indefinite-length map * 63 -- First key, UTF-8 string length 3 * 46756e -- "Fun" * F5 -- First value, true @@ -260,7 +261,7 @@ namespace Aws * * Notes: this function will not ensure the data item is well-formed. * - * @return success/failure + * @return true if the operation succeed, false otherwise and LastError() will contain the errorCode. */ bool ConsumeNextWholeDataItem() noexcept; @@ -271,7 +272,7 @@ namespace Aws * 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 + * BF -- Start indefinite-length map * 63 -- First key, UTF-8 string length 3 * 46756e -- "Fun" * F5 -- First value, true @@ -280,7 +281,7 @@ namespace Aws * 21 -- Second value, -2 * FF -- "break" * - * @return success/failure + * @return true if the operation succeed, false otherwise and LastError() will contain the errorCode. */ bool ConsumeNextSingleElement() noexcept; @@ -288,21 +289,22 @@ namespace Aws * Get the next element based on the type. If the next element doesn't match the expected type, an error * will be raised. If the next element has already been cached, it will consume the cached item when no * error was returned. Specifically: - * - Uint - PopNextUnsignedIntVal + * - UInt - PopNextUnsignedIntVal * - NegInt - PopNextNegativeIntVal, it represents (-1 - &out) * - Float - PopNextFloatVal * - Bytes - PopNextBytesVal * - Text - PopNextTextVal * - * @param out - * @return success/failure + * @return If successful, return the next element + * If not, return will be none and Aws::Crt::LastError() can be + * used to retrieve CRT error code. */ - bool PopNextUnsignedIntVal(uint64_t &out) noexcept; - bool PopNextNegativeIntVal(uint64_t &out) noexcept; - bool PopNextFloatVal(double &out) noexcept; - bool PopNextBooleanVal(bool &out) noexcept; - bool PopNextBytesVal(ByteCursor &out) noexcept; - bool PopNextTextVal(ByteCursor &out) noexcept; + Optional PopNextUnsignedIntVal() noexcept; + Optional PopNextNegativeIntVal() noexcept; + Optional PopNextFloatVal() noexcept; + Optional PopNextBooleanVal() noexcept; + Optional PopNextBytesVal() noexcept; + Optional PopNextTextVal() noexcept; /** * Get the next ArrayStart element. Only consume the ArrayStart element and set the size of array to @@ -315,10 +317,11 @@ namespace Aws * - Call ConsumeNextSingleElement to pop the indefinite-length start. * - Decode the next data item until Break is read. * - * @param out_size Store the size of array if succeeded. - * @return success/failure + * @return If successful, return the size of array + * If not, return will be none and Aws::Crt::LastError() can be + * used to retrieve CRT error code. */ - bool PopNextArrayStart(uint64_t &out_size) noexcept; + Optional PopNextArrayStart() noexcept; /** * Get the next MapStart element. Only consume the MapStart element and set the size of array to @@ -331,20 +334,22 @@ namespace Aws * - Call ConsumeNextSingleElement to pop the indefinite-length start. * - Decode the next data item until Break is read. * - * @param out_size Store the size of map if succeeded. - * @return success/failure + * @return If successful, return the size of map + * If not, return will be none and Aws::Crt::LastError() can be + * used to retrieve CRT error code. */ - bool PopNextMapStart(uint64_t &out_size) noexcept; + Optional PopNextMapStart() noexcept; /** * Get the next Tag element. Only consume the Tag element and set the tag value to out_tag_val, * not the content of the tagged value. The next CBOR data item will be the content of the tagged value * for a valid CBOR data. * - * @param out_tag_val Store the tag value if succeeded. - * @return success/failure + * @return If successful, return the tag value + * If not, return will be none and Aws::Crt::LastError() can be + * used to retrieve CRT error code. */ - bool PopNextTagVal(uint64_t &out_tag_val) noexcept; + Optional PopNextTagVal() noexcept; /** * @return the value of the last aws error encountered by operations on this instance. diff --git a/source/cbor/Cbor.cpp b/source/cbor/Cbor.cpp index e505dcb73..7061d4f4b 100644 --- a/source/cbor/Cbor.cpp +++ b/source/cbor/Cbor.cpp @@ -35,7 +35,7 @@ namespace Aws aws_cbor_encoder_reset(m_encoder); } - void CborEncoder::WriteUint(uint64_t value) noexcept + void CborEncoder::WriteUInt(uint64_t value) noexcept { aws_cbor_encoder_write_uint(m_encoder, value); } @@ -119,7 +119,7 @@ namespace Aws * CborDecoder * *****************************************************/ - CborDecoder::CborDecoder(Crt::Allocator *allocator, ByteCursor src) noexcept + CborDecoder::CborDecoder(ByteCursor src, Crt::Allocator *allocator) noexcept { m_decoder = aws_cbor_decoder_new(allocator, src); } @@ -134,16 +134,15 @@ namespace Aws return aws_cbor_decoder_get_remaining_length(m_decoder); } - bool CborDecoder::PeekType(CborType &out_type) noexcept + Optional CborDecoder::PeekType() noexcept { enum aws_cbor_type out_type_c = AWS_CBOR_TYPE_UNKNOWN; if (aws_cbor_decoder_peek_type(m_decoder, &out_type_c) != AWS_OP_SUCCESS) { m_lastError = aws_last_error(); - return false; + return Optional(); } - out_type = (CborType)out_type_c; - return true; + return Optional((CborType)out_type_c); } bool CborDecoder::ConsumeNextWholeDataItem() noexcept { @@ -165,94 +164,103 @@ namespace Aws return true; } - bool CborDecoder::PopNextUnsignedIntVal(uint64_t &out) noexcept + Optional CborDecoder::PopNextUnsignedIntVal() noexcept { + uint64_t out = 0; if (aws_cbor_decoder_pop_next_unsigned_int_val(m_decoder, &out) != AWS_OP_SUCCESS) { m_lastError = aws_last_error(); - return false; + return Optional(); } - return true; + return Optional(out); } - bool CborDecoder::PopNextNegativeIntVal(uint64_t &out) noexcept + Optional CborDecoder::PopNextNegativeIntVal() noexcept { + uint64_t out = 0; if (aws_cbor_decoder_pop_next_negative_int_val(m_decoder, &out) != AWS_OP_SUCCESS) { m_lastError = aws_last_error(); - return false; + return Optional(); } - return true; + return Optional(out); } - bool CborDecoder::PopNextFloatVal(double &out) noexcept + Optional CborDecoder::PopNextFloatVal() noexcept { + double out = 0; if (aws_cbor_decoder_pop_next_float_val(m_decoder, &out) != AWS_OP_SUCCESS) { m_lastError = aws_last_error(); - return false; + return Optional(); } - return true; + return Optional(out); } - bool CborDecoder::PopNextBooleanVal(bool &out) noexcept + Optional CborDecoder::PopNextBooleanVal() noexcept { + bool out = false; if (aws_cbor_decoder_pop_next_boolean_val(m_decoder, &out) != AWS_OP_SUCCESS) { m_lastError = aws_last_error(); - return false; + return Optional(); } - return true; + return Optional(out); } - bool CborDecoder::PopNextBytesVal(ByteCursor &out) noexcept + Optional CborDecoder::PopNextBytesVal() noexcept { + ByteCursor out = {0}; if (aws_cbor_decoder_pop_next_bytes_val(m_decoder, &out) != AWS_OP_SUCCESS) { m_lastError = aws_last_error(); - return false; + return Optional(); } - return true; + return Optional(out); } - bool CborDecoder::PopNextTextVal(ByteCursor &out) noexcept + Optional CborDecoder::PopNextTextVal() noexcept { + ByteCursor out = {0}; if (aws_cbor_decoder_pop_next_text_val(m_decoder, &out) != AWS_OP_SUCCESS) { m_lastError = aws_last_error(); - return false; + return Optional(); } - return true; + return Optional(out); } - bool CborDecoder::PopNextArrayStart(uint64_t &out_size) noexcept + Optional CborDecoder::PopNextArrayStart() noexcept { + uint64_t out_size = 0; if (aws_cbor_decoder_pop_next_array_start(m_decoder, &out_size) != AWS_OP_SUCCESS) { m_lastError = aws_last_error(); - return false; + return Optional(); } - return true; + return Optional(out_size); } - bool CborDecoder::PopNextMapStart(uint64_t &out_size) noexcept + Optional CborDecoder::PopNextMapStart() noexcept { + uint64_t out_size = 0; if (aws_cbor_decoder_pop_next_map_start(m_decoder, &out_size) != AWS_OP_SUCCESS) { m_lastError = aws_last_error(); - return false; + return Optional(); } - return true; + return Optional(out_size); } - bool CborDecoder::PopNextTagVal(uint64_t &out_tag_val) noexcept + Optional CborDecoder::PopNextTagVal() noexcept { + uint64_t out_tag_val = 0; if (aws_cbor_decoder_pop_next_tag_val(m_decoder, &out_tag_val) != AWS_OP_SUCCESS) { m_lastError = aws_last_error(); - return false; + return Optional(); } - return true; + return Optional(out_tag_val); } } // namespace Cbor diff --git a/tests/CborTest.cpp b/tests/CborTest.cpp index 80a979914..f6fa95370 100644 --- a/tests/CborTest.cpp +++ b/tests/CborTest.cpp @@ -29,7 +29,7 @@ static int s_CborSanityTest(struct aws_allocator *allocator, void *ctx) auto expected_tag_val = 999ULL; bool expected_bool_val = true; - encoder.WriteUint(expected_uint_val); + encoder.WriteUInt(expected_uint_val); encoder.WriteNegInt(expected_negint_val); encoder.WriteFloat(expected_float_val); encoder.WriteBytes(expected_bytes_val); @@ -48,80 +48,71 @@ static int s_CborSanityTest(struct aws_allocator *allocator, void *ctx) ByteCursor cursor = encoder.GetEncodedData(); - Cbor::CborDecoder decoder(allocator, cursor); - Cbor::CborType out_type = Cbor::CborType::Unknown; + Cbor::CborDecoder decoder(cursor, allocator); - // Check for Uint - uint64_t uint_val; - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Uint); - ASSERT_TRUE(decoder.PopNextUnsignedIntVal(uint_val) && uint_val == expected_uint_val); + // Check for UInt + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::UInt); + ASSERT_TRUE(decoder.PopNextUnsignedIntVal().value() == expected_uint_val); // Check for NegInt - uint64_t negint_val; - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::NegInt); - ASSERT_TRUE(decoder.PopNextNegativeIntVal(negint_val) && negint_val == expected_negint_val); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::NegInt); + ASSERT_TRUE(decoder.PopNextNegativeIntVal().value() == expected_negint_val); // Check for Float - double float_val; - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Float); - ASSERT_TRUE(decoder.PopNextFloatVal(float_val) && float_val == expected_float_val); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::Float); + ASSERT_TRUE(decoder.PopNextFloatVal().value() == expected_float_val); // Check for Bytes - ByteCursor bytes_val; - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Bytes); - ASSERT_TRUE(decoder.PopNextBytesVal(bytes_val) && aws_byte_cursor_eq(&bytes_val, &expected_bytes_val)); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::Bytes); + ByteCursor decoded_val = decoder.PopNextBytesVal().value(); + ASSERT_TRUE(aws_byte_cursor_eq(&decoded_val, &expected_bytes_val)); // Check for Text - ByteCursor text_val; - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Text); - ASSERT_TRUE(decoder.PopNextTextVal(text_val) && aws_byte_cursor_eq(&text_val, &expected_text_val)); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::Text); + decoded_val = decoder.PopNextTextVal().value(); + ASSERT_TRUE(aws_byte_cursor_eq(&decoded_val, &expected_text_val)); // Check for ArrayStart - uint64_t array_size; - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::ArrayStart); - ASSERT_TRUE(decoder.PopNextArrayStart(array_size) && array_size == expected_array_size); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::ArrayStart); + ASSERT_TRUE(decoder.PopNextArrayStart().value() == expected_array_size); // Check for MapStart - uint64_t map_size; - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::MapStart); - ASSERT_TRUE(decoder.PopNextMapStart(map_size) && map_size == expected_map_size); - + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::MapStart); + ASSERT_TRUE(decoder.PopNextMapStart().value() == expected_map_size); // Check for Tag - uint64_t tag_val; - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Tag); - ASSERT_TRUE(decoder.PopNextTagVal(tag_val) && tag_val == expected_tag_val); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::Tag); + ASSERT_TRUE(decoder.PopNextTagVal().value() == expected_tag_val); // Check for Bool - bool bool_val; - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Bool); - ASSERT_TRUE(decoder.PopNextBooleanVal(bool_val) && bool_val == expected_bool_val); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::Bool); + ASSERT_TRUE(decoder.PopNextBooleanVal().value() == expected_bool_val); // Check for Null - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Null); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::Null); ASSERT_TRUE(decoder.ConsumeNextWholeDataItem()); // Check for Undefined - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Undefined); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::Undefined); ASSERT_TRUE(decoder.ConsumeNextWholeDataItem()); // Check for Break - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::Break); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::Break); ASSERT_TRUE(decoder.ConsumeNextSingleElement()); // Check for IndefBytesStart - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::IndefBytesStart); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::IndefBytesStart); ASSERT_TRUE(decoder.ConsumeNextSingleElement()); // Check for IndefTextStart - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::IndefTextStart); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::IndefTextStart); ASSERT_TRUE(decoder.ConsumeNextSingleElement()); // Check for IndefArrayStart - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::IndefArrayStart); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::IndefArrayStart); ASSERT_TRUE(decoder.ConsumeNextSingleElement()); // Check for IndefMapStart - ASSERT_TRUE(decoder.PeekType(out_type) && out_type == Cbor::CborType::IndefMapStart); + ASSERT_TRUE(decoder.PeekType().value() == Cbor::CborType::IndefMapStart); ASSERT_TRUE(decoder.ConsumeNextSingleElement()); auto length = decoder.GetRemainingLength(); @@ -148,63 +139,59 @@ static void s_encode_timestamp_helper( static int s_decode_timestamp_helper(Cbor::CborDecoder &decoder, std::chrono::system_clock::time_point &outTimePoint) { - Cbor::CborType out_type = Cbor::CborType::Unknown; - if (!decoder.PeekType(out_type) && out_type != Cbor::CborType::Tag) + if (decoder.PeekType().value() != Cbor::CborType::Tag) { return AWS_OP_ERR; } - uint64_t tag_val = 0; - if (!decoder.PopNextTagVal(tag_val) && tag_val != AWS_CBOR_TAG_EPOCH_TIME) + if (decoder.PopNextTagVal().value() != AWS_CBOR_TAG_EPOCH_TIME) { return AWS_OP_ERR; } - if (decoder.PeekType(out_type)) + Cbor::CborType out_type = decoder.PeekType().value(); + switch (out_type) { - switch (out_type) + case Cbor::CborType::UInt: + case Cbor::CborType::NegInt: { - case Cbor::CborType::Uint: - case Cbor::CborType::NegInt: + int64_t secs_timestamp = 0; + uint64_t unsigned_val = 0; + if (out_type == Cbor::CborType::UInt) { - int64_t secs_timestamp = 0; - uint64_t unsigned_val = 0; - if (out_type == Cbor::CborType::Uint) - { - decoder.PopNextUnsignedIntVal(unsigned_val); - if (unsigned_val > INT64_MAX) - { - /* Overflow */ - return AWS_OP_ERR; - } - secs_timestamp = (int64_t)unsigned_val; - } - else + unsigned_val = decoder.PopNextUnsignedIntVal().value(); + if (unsigned_val > INT64_MAX) { - decoder.PopNextNegativeIntVal(unsigned_val); - if (unsigned_val > INT64_MAX) - { - /* Overflow */ - return AWS_OP_ERR; - } - /* convert the encoded number to real negative number */ - secs_timestamp = (int64_t)(-1 - unsigned_val); + /* Overflow */ + return AWS_OP_ERR; } - std::chrono::duration timestamp(secs_timestamp); - outTimePoint = std::chrono::system_clock::time_point(timestamp); - return AWS_OP_SUCCESS; + secs_timestamp = (int64_t)unsigned_val; } - case Cbor::CborType::Float: + else { - double double_val = 0; - decoder.PopNextFloatVal(double_val); - std::chrono::duration timestamp(double_val); - outTimePoint = std::chrono::system_clock::time_point( - std::chrono::duration_cast(timestamp)); - return AWS_OP_SUCCESS; + unsigned_val = decoder.PopNextNegativeIntVal().value(); + if (unsigned_val > INT64_MAX) + { + /* Overflow */ + return AWS_OP_ERR; + } + /* convert the encoded number to real negative number */ + secs_timestamp = (int64_t)(-1 - unsigned_val); } - default: - break; + std::chrono::duration timestamp(secs_timestamp); + outTimePoint = std::chrono::system_clock::time_point(timestamp); + return AWS_OP_SUCCESS; + } + case Cbor::CborType::Float: + { + double double_val = decoder.PopNextFloatVal().value(); + std::chrono::duration timestamp(double_val); + outTimePoint = + std::chrono::system_clock::time_point(std::chrono::duration_cast(timestamp)); + return AWS_OP_SUCCESS; } + default: + break; } + return AWS_OP_ERR; } @@ -239,7 +226,7 @@ static int s_CborTimeStampTest(struct aws_allocator *allocator, void *ctx) s_encode_timestamp_helper(encoder, now); ByteCursor cursor = encoder.GetEncodedData(); - Cbor::CborDecoder decoder(allocator, cursor); + Cbor::CborDecoder decoder(cursor); std::chrono::time_point decoded; ASSERT_SUCCESS(s_decode_timestamp_helper(decoder, decoded)); From f9eeff98a24d656dcc2723eec9074c9da6d7bce6 Mon Sep 17 00:00:00 2001 From: Dengke Date: Thu, 20 Jun 2024 14:33:00 -0700 Subject: [PATCH 8/9] use the tag --- crt/aws-c-common | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crt/aws-c-common b/crt/aws-c-common index 7210cf204..6d974f92c 160000 --- a/crt/aws-c-common +++ b/crt/aws-c-common @@ -1 +1 @@ -Subproject commit 7210cf20440182d10b8cf58d876f1ab7dfe8d165 +Subproject commit 6d974f92c1d86391c1dcb1173239adf757c52b2d From 36254a2b6e4a1c9c085013b0449ba537d5f13604 Mon Sep 17 00:00:00 2001 From: Dengke Date: Mon, 24 Jun 2024 10:58:27 -0700 Subject: [PATCH 9/9] use the local last error --- include/aws/crt/cbor/Cbor.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/aws/crt/cbor/Cbor.h b/include/aws/crt/cbor/Cbor.h index 3cc120cb7..af3f82716 100644 --- a/include/aws/crt/cbor/Cbor.h +++ b/include/aws/crt/cbor/Cbor.h @@ -239,7 +239,7 @@ namespace Aws * If there was an element cached, just return the type of the cached element. * * @return If successful, return the type of next element - * If not, return will be none and Aws::Crt::LastError() can be + * If not, return will be none and LastError() can be * used to retrieve CRT error code. */ Optional PeekType() noexcept; @@ -296,7 +296,7 @@ namespace Aws * - Text - PopNextTextVal * * @return If successful, return the next element - * If not, return will be none and Aws::Crt::LastError() can be + * If not, return will be none and LastError() can be * used to retrieve CRT error code. */ Optional PopNextUnsignedIntVal() noexcept; @@ -318,7 +318,7 @@ namespace Aws * - Decode the next data item until Break is read. * * @return If successful, return the size of array - * If not, return will be none and Aws::Crt::LastError() can be + * If not, return will be none and LastError() can be * used to retrieve CRT error code. */ Optional PopNextArrayStart() noexcept; @@ -335,7 +335,7 @@ namespace Aws * - Decode the next data item until Break is read. * * @return If successful, return the size of map - * If not, return will be none and Aws::Crt::LastError() can be + * If not, return will be none and LastError() can be * used to retrieve CRT error code. */ Optional PopNextMapStart() noexcept; @@ -346,7 +346,7 @@ namespace Aws * for a valid CBOR data. * * @return If successful, return the tag value - * If not, return will be none and Aws::Crt::LastError() can be + * If not, return will be none and LastError() can be * used to retrieve CRT error code. */ Optional PopNextTagVal() noexcept;