From 3d3fb45cf7f9287366e90c9c22b08a09222c6d34 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Thu, 15 Oct 2026 15:38:56 -0400 Subject: [PATCH 1/5] bignum --- .../client/schema/CborShapeDeserializer.h | 4 + .../client/schema/CborShapeSerializer.h | 4 + .../client/schema/InterceptingSerializer.h | 16 ++ .../client/schema/JsonShapeDeserializer.h | 4 + .../client/schema/JsonShapeSerializer.h | 4 + .../client/schema/QueryShapeSerializer.h | 4 + .../smithy/client/schema/SerdeTraits.h | 11 + .../smithy/client/schema/ShapeDeserializer.h | 6 + .../smithy/client/schema/ShapeSerializer.h | 6 + .../client/schema/SpecificShapeSerializer.h | 4 + .../client/schema/XmlShapeDeserializer.h | 4 + .../smithy/client/schema/XmlShapeSerializer.h | 4 + .../client/schema/CborShapeDeserializer.cpp | 15 ++ .../client/schema/CborShapeSerializer.cpp | 32 +++ .../client/schema/JsonShapeDeserializer.cpp | 29 +++ .../client/schema/JsonShapeSerializer.cpp | 50 ++++ .../client/schema/QueryShapeSerializer.cpp | 50 ++++ .../client/schema/XmlShapeDeserializer.cpp | 26 ++ .../client/schema/XmlShapeSerializer.cpp | 48 ++++ .../schema/CborShapeDeserializerTest.cpp | 227 ++++++++++++++++++ .../client/schema/CborShapeSerializerTest.cpp | 128 ++++++++++ .../smithy/client/schema/CodecTest.cpp | 42 ++++ .../schema/JsonShapeDeserializerTest.cpp | 192 +++++++++++++++ .../client/schema/JsonShapeSerializerTest.cpp | 101 ++++++++ .../schema/QueryShapeSerializerTest.cpp | 122 ++++++++++ .../schema/SchemaSerializerTestHelpers.h | 15 ++ .../schema/XmlShapeDeserializerTest.cpp | 209 ++++++++++++++++ .../client/schema/XmlShapeSerializerTest.cpp | 129 ++++++++++ .../generators/model/CppTypeMapper.java | 49 +++- .../model/BigNumberModelGeneratorTest.java | 93 +++++++ .../generators/model/CppTypeMapperTest.java | 112 +++++++++ .../generators/model/MemberRendererTest.java | 97 ++++++++ 32 files changed, 1829 insertions(+), 8 deletions(-) create mode 100644 tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/BigNumberModelGeneratorTest.java diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/CborShapeDeserializer.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/CborShapeDeserializer.h index 59c0b6663a7..8a710e6cc85 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/CborShapeDeserializer.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/CborShapeDeserializer.h @@ -22,6 +22,10 @@ class SMITHY_API CborShapeDeserializer final : public ShapeDeserializer { Aws::Crt::Optional ReadLong(const Schema& schema) override; Aws::Crt::Optional ReadFloat(const Schema& schema) override; Aws::Crt::Optional ReadDouble(const Schema& schema) override; + Aws::Crt::Optional ReadByte(const Schema& schema) override; + Aws::Crt::Optional ReadShort(const Schema& schema) override; + Aws::Crt::Optional ReadBigInteger(const Schema& schema) override; + Aws::Crt::Optional ReadBigDecimal(const Schema& schema) override; Aws::Crt::Optional ReadString(const Schema& schema) override; Aws::Crt::Optional ReadTimestamp(const Schema& schema) override; Aws::Crt::Optional ReadBlob(const Schema& schema) override; diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/CborShapeSerializer.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/CborShapeSerializer.h index 0335dcf9e34..f66166fbd69 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/CborShapeSerializer.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/CborShapeSerializer.h @@ -25,6 +25,10 @@ class SMITHY_API CborShapeSerializer final : public ShapeSerializer { void WriteLong(const Schema& schema, int64_t value) override; void WriteFloat(const Schema& schema, float value) override; void WriteDouble(const Schema& schema, double value) override; + void WriteByte(const Schema& schema, int8_t value) override; + void WriteShort(const Schema& schema, int16_t value) override; + void WriteBigInteger(const Schema& schema, const Aws::Crt::BigInteger& value) override; + void WriteBigDecimal(const Schema& schema, const Aws::Crt::BigDecimal& value) override; void WriteString(const Schema& schema, const Aws::String& value) override; void WriteTimestamp(const Schema& schema, const Aws::Utils::DateTime& value) override; void WriteBlob(const Schema& schema, const Aws::Utils::ByteBuffer& value) override; diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/InterceptingSerializer.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/InterceptingSerializer.h index 9f7fe78c2f7..b6a7ade25b7 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/InterceptingSerializer.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/InterceptingSerializer.h @@ -44,6 +44,22 @@ class SMITHY_API InterceptingSerializer : public ShapeSerializer { Before(schema).WriteDouble(schema, value); After(schema); } + void WriteByte(const Schema& schema, int8_t value) override { + Before(schema).WriteByte(schema, value); + After(schema); + } + void WriteShort(const Schema& schema, int16_t value) override { + Before(schema).WriteShort(schema, value); + After(schema); + } + void WriteBigInteger(const Schema& schema, const Aws::Crt::BigInteger& value) override { + Before(schema).WriteBigInteger(schema, value); + After(schema); + } + void WriteBigDecimal(const Schema& schema, const Aws::Crt::BigDecimal& value) override { + Before(schema).WriteBigDecimal(schema, value); + After(schema); + } void WriteString(const Schema& schema, const Aws::String& value) override { Before(schema).WriteString(schema, value); After(schema); diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonShapeDeserializer.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonShapeDeserializer.h index 21e94ed3705..e61ca503203 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonShapeDeserializer.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonShapeDeserializer.h @@ -24,6 +24,10 @@ class SMITHY_API JsonShapeDeserializer final : public ShapeDeserializer { Aws::Crt::Optional ReadLong(const Schema& schema) override; Aws::Crt::Optional ReadFloat(const Schema& schema) override; Aws::Crt::Optional ReadDouble(const Schema& schema) override; + Aws::Crt::Optional ReadByte(const Schema& schema) override; + Aws::Crt::Optional ReadShort(const Schema& schema) override; + Aws::Crt::Optional ReadBigInteger(const Schema& schema) override; + Aws::Crt::Optional ReadBigDecimal(const Schema& schema) override; Aws::Crt::Optional ReadString(const Schema& schema) override; Aws::Crt::Optional ReadTimestamp(const Schema& schema) override; Aws::Crt::Optional ReadBlob(const Schema& schema) override; diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonShapeSerializer.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonShapeSerializer.h index 34bb6f4d50a..baca1a19698 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonShapeSerializer.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonShapeSerializer.h @@ -26,6 +26,10 @@ class SMITHY_API JsonShapeSerializer final : public ShapeSerializer { void WriteLong(const Schema& schema, int64_t value) override; void WriteFloat(const Schema& schema, float value) override; void WriteDouble(const Schema& schema, double value) override; + void WriteByte(const Schema& schema, int8_t value) override; + void WriteShort(const Schema& schema, int16_t value) override; + void WriteBigInteger(const Schema& schema, const Aws::Crt::BigInteger& value) override; + void WriteBigDecimal(const Schema& schema, const Aws::Crt::BigDecimal& value) override; void WriteString(const Schema& schema, const Aws::String& value) override; void WriteTimestamp(const Schema& schema, const Aws::Utils::DateTime& value) override; void WriteBlob(const Schema& schema, const Aws::Utils::ByteBuffer& value) override; diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/QueryShapeSerializer.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/QueryShapeSerializer.h index 6b34b501809..3cb5b250fcf 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/QueryShapeSerializer.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/QueryShapeSerializer.h @@ -35,6 +35,10 @@ class SMITHY_API QueryShapeSerializer final : public ShapeSerializer { void WriteLong(const Schema& schema, int64_t value) override; void WriteFloat(const Schema& schema, float value) override; void WriteDouble(const Schema& schema, double value) override; + void WriteByte(const Schema& schema, int8_t value) override; + void WriteShort(const Schema& schema, int16_t value) override; + void WriteBigInteger(const Schema& schema, const Aws::Crt::BigInteger& value) override; + void WriteBigDecimal(const Schema& schema, const Aws::Crt::BigDecimal& value) override; void WriteString(const Schema& schema, const Aws::String& value) override; void WriteTimestamp(const Schema& schema, const Aws::Utils::DateTime& value) override; void WriteBlob(const Schema& schema, const Aws::Utils::ByteBuffer& value) override; diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/SerdeTraits.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/SerdeTraits.h index de3484515da..5f1ce20726d 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/SerdeTraits.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/SerdeTraits.h @@ -7,11 +7,13 @@ #include #include #include +#include #include #include #include #include +#include namespace smithy { namespace schema { @@ -47,6 +49,15 @@ inline TimestampFormatTrait::Format ResolveTimestampFormat(const Schema& schema, return trait ? trait->GetFormat() : protocolDefault; } +// Narrows a decoded integer to T, yielding empty when the value is outside T's range rather than wrapping it. +template +Aws::Crt::Optional NarrowInteger(const Aws::Crt::Optional& value) { + if (!value.has_value() || *value < std::numeric_limits::min() || *value > std::numeric_limits::max()) { + return {}; + } + return static_cast(*value); +} + inline Aws::String EpochSecondsText(const Aws::Utils::DateTime& value) { const int64_t millis = value.Millis(); const bool negative = millis < 0; diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/ShapeDeserializer.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/ShapeDeserializer.h index 22f735fc92b..db47f2d0802 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/ShapeDeserializer.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/ShapeDeserializer.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -34,6 +36,10 @@ class SMITHY_API ShapeDeserializer { virtual Aws::Crt::Optional ReadLong(const Schema& schema) = 0; virtual Aws::Crt::Optional ReadFloat(const Schema& schema) = 0; virtual Aws::Crt::Optional ReadDouble(const Schema& schema) = 0; + virtual Aws::Crt::Optional ReadByte(const Schema& schema) = 0; + virtual Aws::Crt::Optional ReadShort(const Schema& schema) = 0; + virtual Aws::Crt::Optional ReadBigInteger(const Schema& schema) = 0; + virtual Aws::Crt::Optional ReadBigDecimal(const Schema& schema) = 0; virtual Aws::Crt::Optional ReadString(const Schema& schema) = 0; virtual Aws::Crt::Optional ReadTimestamp(const Schema& schema) = 0; virtual Aws::Crt::Optional ReadBlob(const Schema& schema) = 0; diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/ShapeSerializer.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/ShapeSerializer.h index f09509ee2ee..f6956ba0f7f 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/ShapeSerializer.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/ShapeSerializer.h @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -31,6 +33,10 @@ class SMITHY_API ShapeSerializer { virtual void WriteLong(const Schema& schema, int64_t value) = 0; virtual void WriteFloat(const Schema& schema, float value) = 0; virtual void WriteDouble(const Schema& schema, double value) = 0; + virtual void WriteByte(const Schema& schema, int8_t value) = 0; + virtual void WriteShort(const Schema& schema, int16_t value) = 0; + virtual void WriteBigInteger(const Schema& schema, const Aws::Crt::BigInteger& value) = 0; + virtual void WriteBigDecimal(const Schema& schema, const Aws::Crt::BigDecimal& value) = 0; virtual void WriteString(const Schema& schema, const Aws::String& value) = 0; virtual void WriteTimestamp(const Schema& schema, const Aws::Utils::DateTime& value) = 0; virtual void WriteBlob(const Schema& schema, const Aws::Utils::ByteBuffer& value) = 0; diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/SpecificShapeSerializer.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/SpecificShapeSerializer.h index f7fdbc1b17f..bd88ec902c8 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/SpecificShapeSerializer.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/SpecificShapeSerializer.h @@ -20,6 +20,10 @@ class SMITHY_API SpecificShapeSerializer : public ShapeSerializer { void WriteLong(const Schema&, int64_t) override {} void WriteFloat(const Schema&, float) override {} void WriteDouble(const Schema&, double) override {} + void WriteByte(const Schema&, int8_t) override {} + void WriteShort(const Schema&, int16_t) override {} + void WriteBigInteger(const Schema&, const Aws::Crt::BigInteger&) override {} + void WriteBigDecimal(const Schema&, const Aws::Crt::BigDecimal&) override {} void WriteString(const Schema&, const Aws::String&) override {} void WriteTimestamp(const Schema&, const Aws::Utils::DateTime&) override {} void WriteBlob(const Schema&, const Aws::Utils::ByteBuffer&) override {} diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/XmlShapeDeserializer.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/XmlShapeDeserializer.h index 1447e8c8fba..a62a8f64b5b 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/XmlShapeDeserializer.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/XmlShapeDeserializer.h @@ -25,6 +25,10 @@ class SMITHY_API XmlShapeDeserializer final : public ShapeDeserializer { Aws::Crt::Optional ReadLong(const Schema& schema) override; Aws::Crt::Optional ReadFloat(const Schema& schema) override; Aws::Crt::Optional ReadDouble(const Schema& schema) override; + Aws::Crt::Optional ReadByte(const Schema& schema) override; + Aws::Crt::Optional ReadShort(const Schema& schema) override; + Aws::Crt::Optional ReadBigInteger(const Schema& schema) override; + Aws::Crt::Optional ReadBigDecimal(const Schema& schema) override; Aws::Crt::Optional ReadString(const Schema& schema) override; Aws::Crt::Optional ReadTimestamp(const Schema& schema) override; Aws::Crt::Optional ReadBlob(const Schema& schema) override; diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/XmlShapeSerializer.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/XmlShapeSerializer.h index 3a886610f61..a4dcdbe64ae 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/XmlShapeSerializer.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/XmlShapeSerializer.h @@ -26,6 +26,10 @@ class SMITHY_API XmlShapeSerializer final : public ShapeSerializer { void WriteLong(const Schema& schema, int64_t value) override; void WriteFloat(const Schema& schema, float value) override; void WriteDouble(const Schema& schema, double value) override; + void WriteByte(const Schema& schema, int8_t value) override; + void WriteShort(const Schema& schema, int16_t value) override; + void WriteBigInteger(const Schema& schema, const Aws::Crt::BigInteger& value) override; + void WriteBigDecimal(const Schema& schema, const Aws::Crt::BigDecimal& value) override; void WriteString(const Schema& schema, const Aws::String& value) override; void WriteTimestamp(const Schema& schema, const Aws::Utils::DateTime& value) override; void WriteBlob(const Schema& schema, const Aws::Utils::ByteBuffer& value) override; diff --git a/src/aws-cpp-sdk-core/source/smithy/client/schema/CborShapeDeserializer.cpp b/src/aws-cpp-sdk-core/source/smithy/client/schema/CborShapeDeserializer.cpp index 55c84193a09..fb0876d2f05 100644 --- a/src/aws-cpp-sdk-core/source/smithy/client/schema/CborShapeDeserializer.cpp +++ b/src/aws-cpp-sdk-core/source/smithy/client/schema/CborShapeDeserializer.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace smithy::schema; using namespace Aws::Utils; @@ -137,6 +138,12 @@ class CborShapeDeserializer::Impl final : public ShapeDeserializer { return static_cast(longVal.value()); } + Aws::Crt::Optional ReadByte(const Schema& schema) override { return NarrowInteger(ReadLong(schema)); } + Aws::Crt::Optional ReadShort(const Schema& schema) override { return NarrowInteger(ReadLong(schema)); } + Aws::Crt::Optional ReadBigInteger(const Schema&) override { return m_decoder.PopNextBigIntegerVal(); } + + Aws::Crt::Optional ReadBigDecimal(const Schema&) override { return m_decoder.PopNextBigDecimalVal(); } + Aws::Crt::Optional ReadString(const Schema&) override { return ReadText(); } Aws::Crt::Optional ReadTimestamp(const Schema& schema) override { @@ -264,6 +271,14 @@ Aws::Crt::Optional CborShapeDeserializer::ReadInteger(const Schema& schema) Aws::Crt::Optional CborShapeDeserializer::ReadLong(const Schema& schema) { return m_impl->ReadLong(schema); } Aws::Crt::Optional CborShapeDeserializer::ReadFloat(const Schema& schema) { return m_impl->ReadFloat(schema); } Aws::Crt::Optional CborShapeDeserializer::ReadDouble(const Schema& schema) { return m_impl->ReadDouble(schema); } +Aws::Crt::Optional CborShapeDeserializer::ReadByte(const Schema& schema) { return m_impl->ReadByte(schema); } +Aws::Crt::Optional CborShapeDeserializer::ReadShort(const Schema& schema) { return m_impl->ReadShort(schema); } +Aws::Crt::Optional CborShapeDeserializer::ReadBigInteger(const Schema& schema) { + return m_impl->ReadBigInteger(schema); +} +Aws::Crt::Optional CborShapeDeserializer::ReadBigDecimal(const Schema& schema) { + return m_impl->ReadBigDecimal(schema); +} Aws::Crt::Optional CborShapeDeserializer::ReadString(const Schema& schema) { return m_impl->ReadString(schema); } Aws::Crt::Optional CborShapeDeserializer::ReadTimestamp(const Schema& schema) { return m_impl->ReadTimestamp(schema); } Aws::Crt::Optional CborShapeDeserializer::ReadBlob(const Schema& schema) { return m_impl->ReadBlob(schema); } diff --git a/src/aws-cpp-sdk-core/source/smithy/client/schema/CborShapeSerializer.cpp b/src/aws-cpp-sdk-core/source/smithy/client/schema/CborShapeSerializer.cpp index a76038845b9..c7385fcb7fc 100644 --- a/src/aws-cpp-sdk-core/source/smithy/client/schema/CborShapeSerializer.cpp +++ b/src/aws-cpp-sdk-core/source/smithy/client/schema/CborShapeSerializer.cpp @@ -40,6 +40,10 @@ class CborShapeSerializer::Impl final : public ShapeSerializer { void WriteLong(const Schema&, int64_t value) override { WriteIntValue(value); } void WriteFloat(const Schema&, float value) override { m_encoder.WriteFloat(value); } void WriteDouble(const Schema&, double value) override { m_encoder.WriteFloat(value); } + void WriteByte(const Schema&, int8_t value) override { WriteIntValue(value); } + void WriteShort(const Schema&, int16_t value) override { WriteIntValue(value); } + void WriteBigInteger(const Schema&, const Aws::Crt::BigInteger& value) override { m_encoder.WriteBigInteger(value); } + void WriteBigDecimal(const Schema&, const Aws::Crt::BigDecimal& value) override { m_encoder.WriteBigDecimal(value); } void WriteString(const Schema&, const Aws::String& value) override { WriteText(value); } void WriteTimestamp(const Schema&, const DateTime& value) override { m_encoder.WriteTag(1); @@ -108,6 +112,22 @@ class CborShapeSerializer::Impl final : public ShapeSerializer { m_outer->WriteMemberKey(s); m_outer->WriteDouble(s, v); } + void WriteByte(const Schema& s, int8_t v) override { + m_outer->WriteMemberKey(s); + m_outer->WriteByte(s, v); + } + void WriteShort(const Schema& s, int16_t v) override { + m_outer->WriteMemberKey(s); + m_outer->WriteShort(s, v); + } + void WriteBigInteger(const Schema& s, const Aws::Crt::BigInteger& v) override { + m_outer->WriteMemberKey(s); + m_outer->WriteBigInteger(s, v); + } + void WriteBigDecimal(const Schema& s, const Aws::Crt::BigDecimal& v) override { + m_outer->WriteMemberKey(s); + m_outer->WriteBigDecimal(s, v); + } void WriteString(const Schema& s, const Aws::String& v) override { m_outer->WriteMemberKey(s); m_outer->WriteString(s, v); @@ -142,6 +162,10 @@ class CborShapeSerializer::Impl final : public ShapeSerializer { void WriteLong(const Schema& s, int64_t v) override { m_outer->WriteLong(s, v); } void WriteFloat(const Schema& s, float v) override { m_outer->WriteFloat(s, v); } void WriteDouble(const Schema& s, double v) override { m_outer->WriteDouble(s, v); } + void WriteByte(const Schema& s, int8_t v) override { m_outer->WriteByte(s, v); } + void WriteShort(const Schema& s, int16_t v) override { m_outer->WriteShort(s, v); } + void WriteBigInteger(const Schema& s, const Aws::Crt::BigInteger& v) override { m_outer->WriteBigInteger(s, v); } + void WriteBigDecimal(const Schema& s, const Aws::Crt::BigDecimal& v) override { m_outer->WriteBigDecimal(s, v); } void WriteString(const Schema& s, const Aws::String& v) override { m_outer->WriteString(s, v); } void WriteTimestamp(const Schema& s, const DateTime& v) override { m_outer->WriteTimestamp(s, v); } void WriteBlob(const Schema& s, const ByteBuffer& v) override { m_outer->WriteBlob(s, v); } @@ -193,6 +217,14 @@ void CborShapeSerializer::WriteInteger(const Schema& schema, int value) { m_impl void CborShapeSerializer::WriteLong(const Schema& schema, int64_t value) { m_impl->WriteLong(schema, value); } void CborShapeSerializer::WriteFloat(const Schema& schema, float value) { m_impl->WriteFloat(schema, value); } void CborShapeSerializer::WriteDouble(const Schema& schema, double value) { m_impl->WriteDouble(schema, value); } +void CborShapeSerializer::WriteByte(const Schema& schema, int8_t value) { m_impl->WriteByte(schema, value); } +void CborShapeSerializer::WriteShort(const Schema& schema, int16_t value) { m_impl->WriteShort(schema, value); } +void CborShapeSerializer::WriteBigInteger(const Schema& schema, const Aws::Crt::BigInteger& value) { + m_impl->WriteBigInteger(schema, value); +} +void CborShapeSerializer::WriteBigDecimal(const Schema& schema, const Aws::Crt::BigDecimal& value) { + m_impl->WriteBigDecimal(schema, value); +} void CborShapeSerializer::WriteString(const Schema& schema, const Aws::String& value) { m_impl->WriteString(schema, value); } void CborShapeSerializer::WriteTimestamp(const Schema& schema, const DateTime& value) { m_impl->WriteTimestamp(schema, value); } void CborShapeSerializer::WriteBlob(const Schema& schema, const ByteBuffer& value) { m_impl->WriteBlob(schema, value); } diff --git a/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonShapeDeserializer.cpp b/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonShapeDeserializer.cpp index 74213f9cf93..6c5fb3ef4b0 100644 --- a/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonShapeDeserializer.cpp +++ b/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonShapeDeserializer.cpp @@ -183,6 +183,27 @@ class JsonShapeDeserializer::Impl final : public ShapeDeserializer { return StringUtils::ParseDouble(ReadNumberToken()); } + Aws::Crt::Optional ReadByte(const Schema& schema) override { return NarrowInteger(ReadLong(schema)); } + Aws::Crt::Optional ReadShort(const Schema& schema) override { return NarrowInteger(ReadLong(schema)); } + + Aws::Crt::Optional ReadBigInteger(const Schema&) override { + if (!IsNumberStart(PeekNonWs())) { + SkipValue(); + return {}; + } + const Aws::String token = ReadNumberToken(); + return Aws::Crt::BigInteger::FromString(Aws::Crt::ByteCursorFromArray(reinterpret_cast(token.data()), token.size())); + } + + Aws::Crt::Optional ReadBigDecimal(const Schema&) override { + if (!IsNumberStart(PeekNonWs())) { + SkipValue(); + return {}; + } + const Aws::String token = ReadNumberToken(); + return Aws::Crt::BigDecimal::FromString(Aws::Crt::ByteCursorFromArray(reinterpret_cast(token.data()), token.size())); + } + Aws::Crt::Optional ReadString(const Schema&) override { if (PeekNonWs() != '"') { SkipValue(); @@ -619,6 +640,14 @@ Aws::Crt::Optional JsonShapeDeserializer::ReadInteger(const Schema& schema) Aws::Crt::Optional JsonShapeDeserializer::ReadLong(const Schema& schema) { return m_impl->ReadLong(schema); } Aws::Crt::Optional JsonShapeDeserializer::ReadFloat(const Schema& schema) { return m_impl->ReadFloat(schema); } Aws::Crt::Optional JsonShapeDeserializer::ReadDouble(const Schema& schema) { return m_impl->ReadDouble(schema); } +Aws::Crt::Optional JsonShapeDeserializer::ReadByte(const Schema& schema) { return m_impl->ReadByte(schema); } +Aws::Crt::Optional JsonShapeDeserializer::ReadShort(const Schema& schema) { return m_impl->ReadShort(schema); } +Aws::Crt::Optional JsonShapeDeserializer::ReadBigInteger(const Schema& schema) { + return m_impl->ReadBigInteger(schema); +} +Aws::Crt::Optional JsonShapeDeserializer::ReadBigDecimal(const Schema& schema) { + return m_impl->ReadBigDecimal(schema); +} Aws::Crt::Optional JsonShapeDeserializer::ReadString(const Schema& schema) { return m_impl->ReadString(schema); } Aws::Crt::Optional JsonShapeDeserializer::ReadTimestamp(const Schema& schema) { return m_impl->ReadTimestamp(schema); } Aws::Crt::Optional JsonShapeDeserializer::ReadBlob(const Schema& schema) { return m_impl->ReadBlob(schema); } diff --git a/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonShapeSerializer.cpp b/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonShapeSerializer.cpp index 25f72255d5f..79a5a50deb6 100644 --- a/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonShapeSerializer.cpp +++ b/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonShapeSerializer.cpp @@ -71,6 +71,16 @@ class JsonShapeSerializer::Impl final : public ShapeSerializer { } } void WriteDouble(const Schema&, double value) override { WriteFloatingPoint(value); } + void WriteByte(const Schema&, int8_t value) override { m_buf += StringUtils::to_string(static_cast(value)); } + void WriteShort(const Schema&, int16_t value) override { m_buf += StringUtils::to_string(static_cast(value)); } + void WriteBigInteger(const Schema&, const Aws::Crt::BigInteger& value) override { + const auto text = value.ToString(); + m_buf.append(text.data(), text.size()); + } + void WriteBigDecimal(const Schema&, const Aws::Crt::BigDecimal& value) override { + const auto text = value.ToString(); + m_buf.append(text.data(), text.size()); + } void WriteString(const Schema&, const Aws::String& value) override { Aws::Schema::WriteQuotedJsonString(m_buf, value); } void WriteTimestamp(const Schema& schema, const DateTime& value) override { const auto format = ResolveTimestampFormat(schema, m_settings.GetDefaultTimestampFormat()); @@ -207,6 +217,22 @@ class JsonShapeSerializer::Impl final : public ShapeSerializer { m_outer->WriteFieldName(s); m_outer->WriteDouble(s, v); } + void WriteByte(const Schema& s, int8_t v) override { + m_outer->WriteFieldName(s); + m_outer->WriteByte(s, v); + } + void WriteShort(const Schema& s, int16_t v) override { + m_outer->WriteFieldName(s); + m_outer->WriteShort(s, v); + } + void WriteBigInteger(const Schema& s, const Aws::Crt::BigInteger& v) override { + m_outer->WriteFieldName(s); + m_outer->WriteBigInteger(s, v); + } + void WriteBigDecimal(const Schema& s, const Aws::Crt::BigDecimal& v) override { + m_outer->WriteFieldName(s); + m_outer->WriteBigDecimal(s, v); + } void WriteString(const Schema& s, const Aws::String& v) override { m_outer->WriteFieldName(s); m_outer->WriteString(s, v); @@ -268,6 +294,22 @@ class JsonShapeSerializer::Impl final : public ShapeSerializer { m_outer->WriteCommaIfNeeded(); m_outer->WriteDouble(s, v); } + void WriteByte(const Schema& s, int8_t v) override { + m_outer->WriteCommaIfNeeded(); + m_outer->WriteByte(s, v); + } + void WriteShort(const Schema& s, int16_t v) override { + m_outer->WriteCommaIfNeeded(); + m_outer->WriteShort(s, v); + } + void WriteBigInteger(const Schema& s, const Aws::Crt::BigInteger& v) override { + m_outer->WriteCommaIfNeeded(); + m_outer->WriteBigInteger(s, v); + } + void WriteBigDecimal(const Schema& s, const Aws::Crt::BigDecimal& v) override { + m_outer->WriteCommaIfNeeded(); + m_outer->WriteBigDecimal(s, v); + } void WriteString(const Schema& s, const Aws::String& v) override { m_outer->WriteCommaIfNeeded(); m_outer->WriteString(s, v); @@ -350,6 +392,14 @@ void JsonShapeSerializer::WriteInteger(const Schema& schema, int value) { m_impl void JsonShapeSerializer::WriteLong(const Schema& schema, int64_t value) { m_impl->WriteLong(schema, value); } void JsonShapeSerializer::WriteFloat(const Schema& schema, float value) { m_impl->WriteFloat(schema, value); } void JsonShapeSerializer::WriteDouble(const Schema& schema, double value) { m_impl->WriteDouble(schema, value); } +void JsonShapeSerializer::WriteByte(const Schema& schema, int8_t value) { m_impl->WriteByte(schema, value); } +void JsonShapeSerializer::WriteShort(const Schema& schema, int16_t value) { m_impl->WriteShort(schema, value); } +void JsonShapeSerializer::WriteBigInteger(const Schema& schema, const Aws::Crt::BigInteger& value) { + m_impl->WriteBigInteger(schema, value); +} +void JsonShapeSerializer::WriteBigDecimal(const Schema& schema, const Aws::Crt::BigDecimal& value) { + m_impl->WriteBigDecimal(schema, value); +} void JsonShapeSerializer::WriteString(const Schema& schema, const Aws::String& value) { m_impl->WriteString(schema, value); } void JsonShapeSerializer::WriteTimestamp(const Schema& schema, const DateTime& value) { m_impl->WriteTimestamp(schema, value); } void JsonShapeSerializer::WriteBlob(const Schema& schema, const ByteBuffer& value) { m_impl->WriteBlob(schema, value); } diff --git a/src/aws-cpp-sdk-core/source/smithy/client/schema/QueryShapeSerializer.cpp b/src/aws-cpp-sdk-core/source/smithy/client/schema/QueryShapeSerializer.cpp index 7a3725accae..9c0caa54d58 100644 --- a/src/aws-cpp-sdk-core/source/smithy/client/schema/QueryShapeSerializer.cpp +++ b/src/aws-cpp-sdk-core/source/smithy/client/schema/QueryShapeSerializer.cpp @@ -61,6 +61,16 @@ class QueryShapeSerializer::Impl final : public ShapeSerializer { void WriteLong(const Schema&, int64_t value) override { EmitField(StringUtils::to_string(value)); } void WriteFloat(const Schema&, float value) override { EmitField(FormatFloatingPoint(value)); } void WriteDouble(const Schema&, double value) override { EmitField(FormatFloatingPoint(value)); } + void WriteByte(const Schema&, int8_t value) override { EmitField(StringUtils::to_string(static_cast(value))); } + void WriteShort(const Schema&, int16_t value) override { EmitField(StringUtils::to_string(static_cast(value))); } + void WriteBigInteger(const Schema&, const Aws::Crt::BigInteger& value) override { + const auto text = value.ToString(); + EmitField(Aws::String(text.data(), text.size())); + } + void WriteBigDecimal(const Schema&, const Aws::Crt::BigDecimal& value) override { + const auto text = value.ToString(); + EmitField(Aws::String(text.data(), text.size())); + } void WriteString(const Schema&, const Aws::String& value) override { EmitField(StringUtils::URLEncode(value.c_str())); } void WriteTimestamp(const Schema& schema, const DateTime& value) override { EmitField(StringUtils::URLEncode( @@ -123,6 +133,22 @@ class QueryShapeSerializer::Impl final : public ShapeSerializer { m_outer->m_prefix = ChildKey(s); m_outer->WriteDouble(s, v); } + void WriteByte(const Schema& s, int8_t v) override { + m_outer->m_prefix = ChildKey(s); + m_outer->WriteByte(s, v); + } + void WriteShort(const Schema& s, int16_t v) override { + m_outer->m_prefix = ChildKey(s); + m_outer->WriteShort(s, v); + } + void WriteBigInteger(const Schema& s, const Aws::Crt::BigInteger& v) override { + m_outer->m_prefix = ChildKey(s); + m_outer->WriteBigInteger(s, v); + } + void WriteBigDecimal(const Schema& s, const Aws::Crt::BigDecimal& v) override { + m_outer->m_prefix = ChildKey(s); + m_outer->WriteBigDecimal(s, v); + } void WriteString(const Schema& s, const Aws::String& v) override { m_outer->m_prefix = ChildKey(s); m_outer->WriteString(s, v); @@ -182,6 +208,22 @@ class QueryShapeSerializer::Impl final : public ShapeSerializer { m_outer->m_prefix = ItemKey(); m_outer->WriteDouble(s, v); } + void WriteByte(const Schema& s, int8_t v) override { + m_outer->m_prefix = ItemKey(); + m_outer->WriteByte(s, v); + } + void WriteShort(const Schema& s, int16_t v) override { + m_outer->m_prefix = ItemKey(); + m_outer->WriteShort(s, v); + } + void WriteBigInteger(const Schema& s, const Aws::Crt::BigInteger& v) override { + m_outer->m_prefix = ItemKey(); + m_outer->WriteBigInteger(s, v); + } + void WriteBigDecimal(const Schema& s, const Aws::Crt::BigDecimal& v) override { + m_outer->m_prefix = ItemKey(); + m_outer->WriteBigDecimal(s, v); + } void WriteString(const Schema& s, const Aws::String& v) override { m_outer->m_prefix = ItemKey(); m_outer->WriteString(s, v); @@ -355,6 +397,14 @@ void QueryShapeSerializer::WriteInteger(const Schema& schema, int value) { m_imp void QueryShapeSerializer::WriteLong(const Schema& schema, int64_t value) { m_impl->WriteLong(schema, value); } void QueryShapeSerializer::WriteFloat(const Schema& schema, float value) { m_impl->WriteFloat(schema, value); } void QueryShapeSerializer::WriteDouble(const Schema& schema, double value) { m_impl->WriteDouble(schema, value); } +void QueryShapeSerializer::WriteByte(const Schema& schema, int8_t value) { m_impl->WriteByte(schema, value); } +void QueryShapeSerializer::WriteShort(const Schema& schema, int16_t value) { m_impl->WriteShort(schema, value); } +void QueryShapeSerializer::WriteBigInteger(const Schema& schema, const Aws::Crt::BigInteger& value) { + m_impl->WriteBigInteger(schema, value); +} +void QueryShapeSerializer::WriteBigDecimal(const Schema& schema, const Aws::Crt::BigDecimal& value) { + m_impl->WriteBigDecimal(schema, value); +} void QueryShapeSerializer::WriteString(const Schema& schema, const Aws::String& value) { m_impl->WriteString(schema, value); } void QueryShapeSerializer::WriteTimestamp(const Schema& schema, const DateTime& value) { m_impl->WriteTimestamp(schema, value); } void QueryShapeSerializer::WriteBlob(const Schema& schema, const ByteBuffer& value) { m_impl->WriteBlob(schema, value); } diff --git a/src/aws-cpp-sdk-core/source/smithy/client/schema/XmlShapeDeserializer.cpp b/src/aws-cpp-sdk-core/source/smithy/client/schema/XmlShapeDeserializer.cpp index 8bcd77b2323..a6dbbbd546e 100644 --- a/src/aws-cpp-sdk-core/source/smithy/client/schema/XmlShapeDeserializer.cpp +++ b/src/aws-cpp-sdk-core/source/smithy/client/schema/XmlShapeDeserializer.cpp @@ -161,6 +161,24 @@ class XmlShapeDeserializer::Impl final : public ShapeDeserializer { return StringUtils::ParseDouble(text); } + Aws::Crt::Optional ReadByte(const Schema& schema) override { return NarrowInteger(ReadLong(schema)); } + Aws::Crt::Optional ReadShort(const Schema& schema) override { return NarrowInteger(ReadLong(schema)); } + Aws::Crt::Optional ReadBigInteger(const Schema&) override { + const Aws::String text = CurrentText(); + if (text.empty()) { + return {}; + } + return Aws::Crt::BigInteger::FromString(Aws::Crt::ByteCursorFromArray(reinterpret_cast(text.data()), text.size())); + } + + Aws::Crt::Optional ReadBigDecimal(const Schema&) override { + const Aws::String text = CurrentText(); + if (text.empty()) { + return {}; + } + return Aws::Crt::BigDecimal::FromString(Aws::Crt::ByteCursorFromArray(reinterpret_cast(text.data()), text.size())); + } + Aws::Crt::Optional ReadString(const Schema&) override { return CurrentText(); } Aws::Crt::Optional ReadTimestamp(const Schema& schema) override { @@ -499,6 +517,14 @@ Aws::Crt::Optional XmlShapeDeserializer::ReadInteger(const Schema& schema) Aws::Crt::Optional XmlShapeDeserializer::ReadLong(const Schema& schema) { return m_impl->ReadLong(schema); } Aws::Crt::Optional XmlShapeDeserializer::ReadFloat(const Schema& schema) { return m_impl->ReadFloat(schema); } Aws::Crt::Optional XmlShapeDeserializer::ReadDouble(const Schema& schema) { return m_impl->ReadDouble(schema); } +Aws::Crt::Optional XmlShapeDeserializer::ReadByte(const Schema& schema) { return m_impl->ReadByte(schema); } +Aws::Crt::Optional XmlShapeDeserializer::ReadShort(const Schema& schema) { return m_impl->ReadShort(schema); } +Aws::Crt::Optional XmlShapeDeserializer::ReadBigInteger(const Schema& schema) { + return m_impl->ReadBigInteger(schema); +} +Aws::Crt::Optional XmlShapeDeserializer::ReadBigDecimal(const Schema& schema) { + return m_impl->ReadBigDecimal(schema); +} Aws::Crt::Optional XmlShapeDeserializer::ReadString(const Schema& schema) { return m_impl->ReadString(schema); } Aws::Crt::Optional XmlShapeDeserializer::ReadTimestamp(const Schema& schema) { return m_impl->ReadTimestamp(schema); } Aws::Crt::Optional XmlShapeDeserializer::ReadBlob(const Schema& schema) { return m_impl->ReadBlob(schema); } diff --git a/src/aws-cpp-sdk-core/source/smithy/client/schema/XmlShapeSerializer.cpp b/src/aws-cpp-sdk-core/source/smithy/client/schema/XmlShapeSerializer.cpp index 21897588835..c3b39a2b458 100644 --- a/src/aws-cpp-sdk-core/source/smithy/client/schema/XmlShapeSerializer.cpp +++ b/src/aws-cpp-sdk-core/source/smithy/client/schema/XmlShapeSerializer.cpp @@ -75,6 +75,10 @@ class XmlShapeSerializer::Impl final : public InterceptingSerializer { void WriteLong(const Schema& schema, int64_t value) override; void WriteFloat(const Schema& schema, float value) override; void WriteDouble(const Schema& schema, double value) override; + void WriteByte(const Schema& schema, int8_t value) override; + void WriteShort(const Schema& schema, int16_t value) override; + void WriteBigInteger(const Schema& schema, const Aws::Crt::BigInteger& value) override; + void WriteBigDecimal(const Schema& schema, const Aws::Crt::BigDecimal& value) override; void WriteString(const Schema& schema, const Aws::String& value) override; void WriteTimestamp(const Schema& schema, const Aws::Utils::DateTime& value) override; void WriteBlob(const Schema& schema, const Aws::Utils::ByteBuffer& value) override; @@ -116,6 +120,10 @@ class XmlShapeSerializer::Impl final : public InterceptingSerializer { void WriteLong(const Schema& schema, int64_t value) override; void WriteFloat(const Schema& schema, float value) override; void WriteDouble(const Schema& schema, double value) override; + void WriteByte(const Schema& schema, int8_t value) override; + void WriteShort(const Schema& schema, int16_t value) override; + void WriteBigInteger(const Schema& schema, const Aws::Crt::BigInteger& value) override; + void WriteBigDecimal(const Schema& schema, const Aws::Crt::BigDecimal& value) override; void WriteString(const Schema& schema, const Aws::String& value) override; void WriteTimestamp(const Schema& schema, const Aws::Utils::DateTime& value) override; @@ -363,6 +371,24 @@ void XmlShapeSerializer::Impl::ValueSerializer::WriteDouble(const Schema&, doubl m_outer->AppendRaw(value > 0 ? "Infinity" : "-Infinity"); } } +void XmlShapeSerializer::Impl::ValueSerializer::WriteByte(const Schema&, int8_t value) { + m_outer->ClosePendingTag(); + m_outer->AppendRaw(StringUtils::to_string(static_cast(value))); +} +void XmlShapeSerializer::Impl::ValueSerializer::WriteShort(const Schema&, int16_t value) { + m_outer->ClosePendingTag(); + m_outer->AppendRaw(StringUtils::to_string(static_cast(value))); +} +void XmlShapeSerializer::Impl::ValueSerializer::WriteBigInteger(const Schema&, const Aws::Crt::BigInteger& value) { + m_outer->ClosePendingTag(); + const auto text = value.ToString(); + m_outer->AppendRaw(Aws::String(text.data(), text.size())); +} +void XmlShapeSerializer::Impl::ValueSerializer::WriteBigDecimal(const Schema&, const Aws::Crt::BigDecimal& value) { + m_outer->ClosePendingTag(); + const auto text = value.ToString(); + m_outer->AppendRaw(Aws::String(text.data(), text.size())); +} void XmlShapeSerializer::Impl::ValueSerializer::WriteString(const Schema&, const Aws::String& value) { m_outer->ClosePendingTag(); m_outer->AppendEscaped(value); @@ -427,6 +453,20 @@ void XmlShapeSerializer::Impl::InlineAttributeSerializer::WriteFloat(const Schem void XmlShapeSerializer::Impl::InlineAttributeSerializer::WriteDouble(const Schema& schema, double value) { WriteAttr(schema, StringUtils::to_string(value)); } +void XmlShapeSerializer::Impl::InlineAttributeSerializer::WriteByte(const Schema& schema, int8_t value) { + WriteAttr(schema, StringUtils::to_string(static_cast(value))); +} +void XmlShapeSerializer::Impl::InlineAttributeSerializer::WriteShort(const Schema& schema, int16_t value) { + WriteAttr(schema, StringUtils::to_string(static_cast(value))); +} +void XmlShapeSerializer::Impl::InlineAttributeSerializer::WriteBigInteger(const Schema& schema, const Aws::Crt::BigInteger& value) { + const auto text = value.ToString(); + WriteAttr(schema, Aws::String(text.data(), text.size())); +} +void XmlShapeSerializer::Impl::InlineAttributeSerializer::WriteBigDecimal(const Schema& schema, const Aws::Crt::BigDecimal& value) { + const auto text = value.ToString(); + WriteAttr(schema, Aws::String(text.data(), text.size())); +} void XmlShapeSerializer::Impl::InlineAttributeSerializer::WriteString(const Schema& schema, const Aws::String& value) { WriteAttr(schema, value); } @@ -466,6 +506,14 @@ void XmlShapeSerializer::WriteInteger(const Schema& schema, int value) { m_impl- void XmlShapeSerializer::WriteLong(const Schema& schema, int64_t value) { m_impl->WriteLong(schema, value); } void XmlShapeSerializer::WriteFloat(const Schema& schema, float value) { m_impl->WriteFloat(schema, value); } void XmlShapeSerializer::WriteDouble(const Schema& schema, double value) { m_impl->WriteDouble(schema, value); } +void XmlShapeSerializer::WriteByte(const Schema& schema, int8_t value) { m_impl->WriteByte(schema, value); } +void XmlShapeSerializer::WriteShort(const Schema& schema, int16_t value) { m_impl->WriteShort(schema, value); } +void XmlShapeSerializer::WriteBigInteger(const Schema& schema, const Aws::Crt::BigInteger& value) { + m_impl->WriteBigInteger(schema, value); +} +void XmlShapeSerializer::WriteBigDecimal(const Schema& schema, const Aws::Crt::BigDecimal& value) { + m_impl->WriteBigDecimal(schema, value); +} void XmlShapeSerializer::WriteString(const Schema& schema, const Aws::String& value) { m_impl->WriteString(schema, value); } void XmlShapeSerializer::WriteTimestamp(const Schema& schema, const DateTime& value) { m_impl->WriteTimestamp(schema, value); } void XmlShapeSerializer::WriteBlob(const Schema& schema, const ByteBuffer& value) { m_impl->WriteBlob(schema, value); } diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CborShapeDeserializerTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CborShapeDeserializerTest.cpp index e4d5db052e5..fd1f3352f04 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CborShapeDeserializerTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CborShapeDeserializerTest.cpp @@ -555,3 +555,230 @@ TEST_F(CborShapeDeserializerTest, ReadDocumentConsumesValueSoTrailingMemberSurvi }); EXPECT_EQ(trailing, "x"); } + +namespace { + +Aws::Crt::ByteCursor CursorOf(const unsigned char* data, size_t len) { return Aws::Crt::ByteCursorFromArray(data, len); } + +} // namespace + +TEST_F(CborShapeDeserializerTest, BigIntegerRoundTrip) { + auto root = Schema::StructureBuilder("Root").PutMember("big", Schema::CreateBigInteger("BI")).Build(); + auto big = root->GetMember("big").value(); + const char* const values[] = {"0", + "42", + "-1000", + "18446744073709551615", + "-18446744073709551616", + "18446744073709551616", + "-18446744073709551617", + "123456789012345678901234567890", + "-123456789012345678901234567890"}; + for (const char* text : values) { + SCOPED_TRACE(text); + const auto value = MakeBigInteger(text); + auto payload = Encode(root, [&](ShapeSerializer& ser) { ser.WriteBigInteger(*big, value); }); + + CborShapeDeserializer d(Aws::Crt::ByteCursorFromArray(reinterpret_cast(payload.data()), payload.size())); + Aws::Crt::Optional got; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { got = de.ReadBigInteger(m); }); + ASSERT_TRUE(got.has_value()); + EXPECT_TRUE(got.value() == value); + EXPECT_EQ(got.value().ToString(), value.ToString()); + } +} + +TEST_F(CborShapeDeserializerTest, BigDecimalRoundTrip) { + auto root = Schema::StructureBuilder("Root").PutMember("dec", Schema::CreateBigDecimal("BD")).Build(); + auto dec = root->GetMember("dec").value(); + const char* const values[] = { + "0", "1.5", "1.50", "-0.5", "0.100000000000000000000001", "-12345678901234567890.123456789", "123456789012345678901234567890"}; + for (const char* text : values) { + SCOPED_TRACE(text); + const auto value = MakeBigDecimal(text); + auto payload = Encode(root, [&](ShapeSerializer& ser) { ser.WriteBigDecimal(*dec, value); }); + + CborShapeDeserializer d(Aws::Crt::ByteCursorFromArray(reinterpret_cast(payload.data()), payload.size())); + Aws::Crt::Optional got; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { got = de.ReadBigDecimal(m); }); + ASSERT_TRUE(got.has_value()); + EXPECT_TRUE(got.value() == value); + EXPECT_EQ(got.value().ToString(), value.ToString()); + } +} + +TEST_F(CborShapeDeserializerTest, BigIntegerFromWireForms) { + auto scalar = Schema::CreateBigInteger("BI"); + struct Case { + Aws::String bytes; + const char* expected; + }; + const Case cases[] = { + {Aws::String("\x18\x2A", 2), "42"}, + {Aws::String("\x39\x03\xE7", 3), "-1000"}, + {Aws::String("\xC2\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11), "18446744073709551616"}, + {Aws::String("\xC3\x49\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11), "-18446744073709551617"}, + // a tag 2 value that would have fit in a UInt is still accepted + {Aws::String("\xC2\x41\x2A", 3), "42"}, + }; + for (const auto& c : cases) { + SCOPED_TRACE(c.expected); + CborShapeDeserializer d(Aws::Crt::ByteCursorFromArray(reinterpret_cast(c.bytes.data()), c.bytes.size())); + auto got = d.ReadBigInteger(*scalar); + ASSERT_TRUE(got.has_value()); + EXPECT_EQ(got.value().ToString(), c.expected); + } +} + +TEST_F(CborShapeDeserializerTest, BigDecimalFromWireForm) { + auto scalar = Schema::CreateBigDecimal("BD"); + // tag 4 [-1, 15] + const unsigned char data[] = {0xC4, 0x82, 0x20, 0x0F}; + CborShapeDeserializer d(CursorOf(data, sizeof(data))); + auto got = d.ReadBigDecimal(*scalar); + ASSERT_TRUE(got.has_value()); + EXPECT_EQ(got.value().ToString(), "15e-1"); +} + +TEST_F(CborShapeDeserializerTest, BigNumbersInListAndMap) { + auto intsBuilder = Schema::ListBuilder("Ints"); + auto decsBuilder = Schema::MapBuilder("Decs"); + auto root = Schema::StructureBuilder("Root").PutMember("ints", intsBuilder).PutMember("decs", decsBuilder).Build(); + auto ints = root->GetMember("ints").value(); + auto decs = root->GetMember("decs").value(); + auto intElem = Schema::CreateMember("member", ShapeType::BigInteger); + auto decVal = Schema::CreateMember("value", ShapeType::BigDecimal); + auto payload = Encode(root, [&](ShapeSerializer& ser) { + ser.WriteList(*ints, 2, [&](ShapeSerializer& lser) { + lser.WriteBigInteger(*intElem, MakeBigInteger("123456789012345678901234567890")); + lser.WriteBigInteger(*intElem, MakeBigInteger("-7")); + }); + ser.WriteMap(*decs, 1, [&](MapSerializer& mapSer) { + mapSer.WriteEntry("k", + [&](ShapeSerializer& vser) { vser.WriteBigDecimal(*decVal, MakeBigDecimal("-12345678901234567890.123456789")); }); + }); + }); + + CborShapeDeserializer d(Aws::Crt::ByteCursorFromArray(reinterpret_cast(payload.data()), payload.size())); + Aws::Vector intValues; + Aws::Map decValues; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { + if (m.GetMemberName() == "ints") { + de.ReadList(m, [&](ShapeDeserializer& ede) { + auto v = ede.ReadBigInteger(*intElem); + ASSERT_TRUE(v.has_value()); + intValues.emplace_back(v.value().ToString().c_str()); + }); + } else if (m.GetMemberName() == "decs") { + de.ReadMap(m, [&](const Aws::String& key, ShapeDeserializer& vde) { + auto v = vde.ReadBigDecimal(*decVal); + ASSERT_TRUE(v.has_value()); + decValues[key] = v.value().ToString().c_str(); + }); + } + }); + ASSERT_EQ(intValues.size(), 2u); + EXPECT_EQ(intValues[0], "123456789012345678901234567890"); + EXPECT_EQ(intValues[1], "-7"); + ASSERT_EQ(decValues.size(), 1u); + EXPECT_EQ(decValues["k"], "-12345678901234567890123456789e-9"); +} + +TEST_F(CborShapeDeserializerTest, BigIntegerRejectsWrongTypes) { + auto scalar = Schema::CreateBigInteger("BI"); + const Aws::String inputs[] = { + Aws::String("\x61\x61", 2), // text "a" + Aws::String("\xF5", 1), // true + Aws::String("\xFB\x3F\xF8\x00\x00\x00\x00\x00\x00", 9), // double 1.5 + Aws::String("\xC4\x82\x20\x0F", 4), // decimal fraction + Aws::String("\x82\x01\x02", 3), // array + }; + for (const auto& input : inputs) { + SCOPED_TRACE(static_cast(input[0])); + CborShapeDeserializer d(Aws::Crt::ByteCursorFromArray(reinterpret_cast(input.data()), input.size())); + EXPECT_FALSE(d.ReadBigInteger(*scalar).has_value()); + } +} + +TEST_F(CborShapeDeserializerTest, BigIntegerRejectsEmptyPayload) { + auto scalar = Schema::CreateBigInteger("BI"); + const unsigned char empty[1] = {0}; + CborShapeDeserializer d(CursorOf(empty, 0)); + EXPECT_FALSE(d.ReadBigInteger(*scalar).has_value()); +} + +TEST_F(CborShapeDeserializerTest, BigDecimalRejectsWrongTypes) { + auto scalar = Schema::CreateBigDecimal("BD"); + const Aws::String inputs[] = { + Aws::String("\x0F", 1), // plain uint + Aws::String("\x61\x61", 2), // text "a" + Aws::String("\xC2\x41\x2A", 3), // bignum + Aws::String("\xC4\x81\x01", 3), // decimal fraction with a single element + Aws::String("\xC4\x61\x61", 3), // decimal fraction holding text + }; + for (const auto& input : inputs) { + SCOPED_TRACE(static_cast(input[0])); + CborShapeDeserializer d(Aws::Crt::ByteCursorFromArray(reinterpret_cast(input.data()), input.size())); + EXPECT_FALSE(d.ReadBigDecimal(*scalar).has_value()); + } +} + +TEST_F(CborShapeDeserializerTest, BigNumberNullMemberIsSkipped) { + auto root = Schema::StructureBuilder("Root").PutMember("n", Schema::CreateBigInteger("BI")).Build(); + // {"n": null} + const unsigned char data[] = {0xBF, 0x61, 0x6E, 0xF6, 0xFF}; + CborShapeDeserializer d(CursorOf(data, sizeof(data))); + int calls = 0; + d.ReadStruct(*root, [&](const Schema&, ShapeDeserializer&) { ++calls; }); + EXPECT_EQ(calls, 0); +} + +TEST_F(CborShapeDeserializerTest, ByteAndShortRanges) { + auto root = Schema::StructureBuilder("Root") + .PutMember("b", Schema::CreateByte("B")) + .PutMember("s", Schema::CreateShort("S")) + .PutMember("after", Schema::CreateInteger("I")) + .Build(); + auto bMember = root->GetMember("b").value(); + auto sMember = root->GetMember("s").value(); + auto afterMember = root->GetMember("after").value(); + struct Case { + int64_t byteWire; + bool byteOk; + int64_t shortWire; + bool shortOk; + }; + const Case cases[] = { + {-128, true, -32768, true}, + {127, true, 32767, true}, + {128, false, 32768, false}, + {-129, false, -32769, false}, + }; + for (const auto& c : cases) { + SCOPED_TRACE(c.byteWire); + auto payload = Encode(root, [&](ShapeSerializer& ser) { + ser.WriteLong(*bMember, c.byteWire); + ser.WriteLong(*sMember, c.shortWire); + ser.WriteInteger(*afterMember, 1); + }); + CborShapeDeserializer d(Aws::Crt::ByteCursorFromArray(reinterpret_cast(payload.data()), payload.size())); + Aws::Crt::Optional b; + Aws::Crt::Optional s; + Aws::Crt::Optional after; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { + if (m.GetMemberName() == "b") { + b = de.ReadByte(m); + } else if (m.GetMemberName() == "s") { + s = de.ReadShort(m); + } else { + after = de.ReadInteger(m); + } + }); + ASSERT_EQ(b.has_value(), c.byteOk); + ASSERT_EQ(s.has_value(), c.shortOk); + if (c.byteOk) EXPECT_EQ(b.value(), c.byteWire); + if (c.shortOk) EXPECT_EQ(s.value(), c.shortWire); + ASSERT_TRUE(after.has_value()); + EXPECT_EQ(after.value(), 1); + } +} diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CborShapeSerializerTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CborShapeSerializerTest.cpp index bf26953b844..0edd237dfc9 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CborShapeSerializerTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CborShapeSerializerTest.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -22,6 +23,41 @@ class CborShapeSerializerTest : public Aws::Testing::AwsCppSdkGTestSuite {}; namespace { Aws::String Bytes(const char* data, size_t len) { return Aws::String(data, len); } + +// Parses space separated hex octets, e.g. "BF 61 6E FF". +Aws::String Hex(const char* octets) { + Aws::String out; + const char* p = octets; + while (*p) { + if (*p == ' ') { + ++p; + continue; + } + out += static_cast(std::strtoul(Aws::String(p, 2).c_str(), nullptr, 16)); + p += 2; + } + return out; +} + +Aws::String EncodeBigInteger(const char* decimal) { + CborShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto member = Schema::CreateMember("n", ShapeType::BigInteger); + auto value = MakeBigInteger(decimal); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { ser.WriteBigInteger(*member, value); }); + s.WriteStruct(*root, rootStruct); + return s.GetPayload().GetResult(); +} + +Aws::String EncodeBigDecimal(const char* decimal) { + CborShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto member = Schema::CreateMember("n", ShapeType::BigDecimal); + auto value = MakeBigDecimal(decimal); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { ser.WriteBigDecimal(*member, value); }); + s.WriteStruct(*root, rootStruct); + return s.GetPayload().GetResult(); +} } // namespace TEST_F(CborShapeSerializerTest, EmptyStructure) { @@ -1070,3 +1106,95 @@ TEST_F(CborShapeSerializerTest, TimestampIgnoresTimestampFormatTrait) { ASSERT_TRUE(bOut.IsSuccess()); EXPECT_EQ(aOut.GetResult(), bOut.GetResult()); // trait ignored -> identical bytes } + +// RFC 8949: bignums within [-2^64, 2^64 - 1] use major type 0 / 1, others use tag 2 / 3 over a byte string +// holding the big-endian magnitude (n for tag 2, -1 - n for tag 3). + +TEST_F(CborShapeSerializerTest, BigIntegerSmallIsPlainUInt) { + // BF + text("n") + uint 42 (0x18 0x2A) + FF + EXPECT_EQ(EncodeBigInteger("42"), Hex("BF 61 6E 18 2A FF")); +} + +TEST_F(CborShapeSerializerTest, BigIntegerTwoPow64UsesTag2) { + // tag 2, byte string of 9 bytes: 01 00 00 00 00 00 00 00 00 + EXPECT_EQ(EncodeBigInteger("18446744073709551616"), Hex("BF 61 6E C2 49 01 00 00 00 00 00 00 00 00 FF")); +} + +TEST_F(CborShapeSerializerTest, BigIntegerBelowMinusTwoPow64UsesTag3) { + // -2^64 - 1 => magnitude -1 - n = 2^64 = 01 00 00 00 00 00 00 00 00 + EXPECT_EQ(EncodeBigInteger("-18446744073709551617"), Hex("BF 61 6E C3 49 01 00 00 00 00 00 00 00 00 FF")); +} + +TEST_F(CborShapeSerializerTest, BigDecimalSimpleUsesTag4) { + // 1.5 = 15e-1 => tag 4, array(2) [exponent -1 (0x20), mantissa 15 (0x0F)] + EXPECT_EQ(EncodeBigDecimal("1.5"), Hex("BF 61 6E C4 82 20 0F FF")); +} + +TEST_F(CborShapeSerializerTest, ListOfBigNumbers) { + CborShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto ints = Schema::CreateMember("i", ShapeType::List); + auto decs = Schema::CreateMember("d", ShapeType::List); + auto intElem = Schema::CreateMember("member", ShapeType::BigInteger); + auto decElem = Schema::CreateMember("member", ShapeType::BigDecimal); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteList(*ints, 2, [&](ShapeSerializer& lser) { + lser.WriteBigInteger(*intElem, MakeBigInteger("7")); + lser.WriteBigInteger(*intElem, MakeBigInteger("18446744073709551616")); + }); + ser.WriteList(*decs, 1, [&](ShapeSerializer& lser) { lser.WriteBigDecimal(*decElem, MakeBigDecimal("1.5")); }); + }); + s.WriteStruct(*root, rootStruct); + // BF "i" array(2) 07 tag2(2^64) "d" array(1) tag4[-1, 15] FF + EXPECT_EQ(s.GetPayload().GetResult(), Hex("BF 61 69 82 07 C2 49 01 00 00 00 00 00 00 00 00 61 64 81 C4 82 20 0F FF")); +} + +TEST_F(CborShapeSerializerTest, MapOfBigNumbers) { + CborShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto ints = Schema::CreateMember("i", ShapeType::Map); + auto decs = Schema::CreateMember("d", ShapeType::Map); + auto intVal = Schema::CreateMember("value", ShapeType::BigInteger); + auto decVal = Schema::CreateMember("value", ShapeType::BigDecimal); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteMap(*ints, 1, [&](MapSerializer& mapSer) { + mapSer.WriteEntry("a", [&](ShapeSerializer& vser) { vser.WriteBigInteger(*intVal, MakeBigInteger("-18446744073709551617")); }); + }); + ser.WriteMap(*decs, 1, [&](MapSerializer& mapSer) { + mapSer.WriteEntry("b", [&](ShapeSerializer& vser) { vser.WriteBigDecimal(*decVal, MakeBigDecimal("1.5")); }); + }); + }); + s.WriteStruct(*root, rootStruct); + // BF "i" map(1) "a" tag3(2^64) "d" map(1) "b" tag4[-1, 15] FF + EXPECT_EQ(s.GetPayload().GetResult(), Hex("BF 61 69 A1 61 61 C3 49 01 00 00 00 00 00 00 00 00 61 64 A1 61 62 C4 82 20 0F FF")); +} + +TEST_F(CborShapeSerializerTest, ByteAndShortUseIntegerEncoding) { + struct Case { + ShapeType type; + int value; + const char* expected; + }; + // BF + text("n") + value + FF + const Case cases[] = { + {ShapeType::Byte, 127, "BF 61 6E 18 7F FF"}, + {ShapeType::Byte, -128, "BF 61 6E 38 7F FF"}, + {ShapeType::Short, 32767, "BF 61 6E 19 7F FF FF"}, + {ShapeType::Short, -32768, "BF 61 6E 39 7F FF FF"}, + }; + for (const auto& c : cases) { + SCOPED_TRACE(c.value); + CborShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto member = Schema::CreateMember("n", c.type); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + if (c.type == ShapeType::Byte) { + ser.WriteByte(*member, static_cast(c.value)); + } else { + ser.WriteShort(*member, static_cast(c.value)); + } + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), Hex(c.expected)); + } +} diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CodecTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CodecTest.cpp index aac69ec6e0d..82e1cf7dbfe 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CodecTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CodecTest.cpp @@ -302,6 +302,38 @@ class DocHolder final : public SerializableStruct { std::shared_ptr m_payload = Document::Null(); bool m_hasPayload = false; }; + +void BigNumberRoundTrip(const Codec& codec, const std::shared_ptr& root) { + auto big = root->GetMember("big").value(); + auto dec = root->GetMember("dec").value(); + const auto bigValue = MakeBigInteger("-123456789012345678901234567890"); + const auto decValue = MakeBigDecimal("-12345678901234567890.123456789"); + + LambdaStruct shape(*root, [&](ShapeSerializer& ser) { + ser.WriteBigInteger(*big, bigValue); + ser.WriteBigDecimal(*dec, decValue); + }); + auto payload = codec.Serialize(*root, shape); + ASSERT_TRUE(payload.IsSuccess()); + const Aws::String bytes = payload.GetResult(); + + auto d = codec.CreateDeserializer(Aws::Crt::ByteCursorFromArray(reinterpret_cast(bytes.data()), bytes.size())); + ASSERT_NE(d, nullptr); + Aws::Crt::Optional gotBig; + Aws::Crt::Optional gotDec; + d->ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { + if (m.GetMemberName() == "big") { + gotBig = de.ReadBigInteger(m); + } else if (m.GetMemberName() == "dec") { + gotDec = de.ReadBigDecimal(m); + } + }); + ASSERT_TRUE(gotBig.has_value()); + EXPECT_TRUE(gotBig.value() == bigValue); + ASSERT_TRUE(gotDec.has_value()); + EXPECT_TRUE(gotDec.value() == decValue); +} + } // namespace TEST_F(CodecTest, JsonDocumentMemberRoundTrip) { @@ -468,3 +500,13 @@ TEST_F(CodecTest, JsonDocumentAtReaderDepthLimitStillSerializes) { EXPECT_TRUE(reserialized.IsSuccess()) << "write failed at depth " << depth; } } + + +TEST_F(CodecTest, CborBigNumberRoundTrip) { + auto root = Schema::StructureBuilder("Root") + .PutMember("big", Schema::CreateBigInteger("BI")) + .PutMember("dec", Schema::CreateBigDecimal("BD")) + .Build(); + CborCodec codec; + BigNumberRoundTrip(codec, root); +} diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeDeserializerTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeDeserializerTest.cpp index 7097cc3b726..5fedc397ebd 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeDeserializerTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeDeserializerTest.cpp @@ -747,3 +747,195 @@ TEST_F(JsonShapeDeserializerTest, DocumentNumberTimestampGatedByConfiguredFormat EXPECT_FALSE(doc->AsTimestamp().has_value()); } } + +namespace { + +Aws::Crt::ByteCursor CursorOf(const Aws::String& payload) { + return Aws::Crt::ByteCursorFromArray(reinterpret_cast(payload.data()), payload.size()); +} + +} // namespace + +TEST_F(JsonShapeDeserializerTest, BigIntegerValues) { + auto scalar = Schema::CreateBigInteger("BI"); + struct Case { + const char* json; + const char* expected; + }; + const Case cases[] = { + {"0", "0"}, + {"-0", "0"}, + {"42", "42"}, + {"-42", "-42"}, + {"18446744073709551616", "18446744073709551616"}, + {"-18446744073709551617", "-18446744073709551617"}, + {"-123456789012345678901234567890", "-123456789012345678901234567890"}, + }; + for (const auto& c : cases) { + SCOPED_TRACE(c.json); + JsonShapeDeserializer d(CursorOf(c.json)); + auto got = d.ReadBigInteger(*scalar); + ASSERT_TRUE(got.has_value()); + EXPECT_EQ(got.value().ToString(), c.expected); + } +} + +TEST_F(JsonShapeDeserializerTest, BigDecimalValues) { + auto scalar = Schema::CreateBigDecimal("BD"); + struct Case { + const char* json; + const char* expected; + }; + const Case cases[] = { + {"1.5", "15e-1"}, + {"15e-1", "15e-1"}, + {"1E3", "1e3"}, + {"-0.25", "-25e-2"}, + {"123456789012345678901234567890", "123456789012345678901234567890e0"}, + {"123456789012345678901234567890.5", "1234567890123456789012345678905e-1"}, + }; + for (const auto& c : cases) { + SCOPED_TRACE(c.json); + JsonShapeDeserializer d(CursorOf(c.json)); + auto got = d.ReadBigDecimal(*scalar); + ASSERT_TRUE(got.has_value()); + EXPECT_EQ(got.value().ToString(), c.expected); + } +} + +TEST_F(JsonShapeDeserializerTest, BigNumbersInListAndMap) { + auto intsBuilder = Schema::ListBuilder("Ints"); + auto decsBuilder = Schema::MapBuilder("Decs"); + auto root = Schema::StructureBuilder("Root").PutMember("ints", intsBuilder).PutMember("decs", decsBuilder).Build(); + auto ints = root->GetMember("ints").value(); + auto decs = root->GetMember("decs").value(); + auto intElem = Schema::CreateMember("member", ShapeType::BigInteger); + auto decVal = Schema::CreateMember("value", ShapeType::BigDecimal); + auto payload = Encode(root, [&](ShapeSerializer& ser) { + ser.WriteList(*ints, 2, [&](ShapeSerializer& lser) { + lser.WriteBigInteger(*intElem, MakeBigInteger("123456789012345678901234567890")); + lser.WriteBigInteger(*intElem, MakeBigInteger("-7")); + }); + ser.WriteMap(*decs, 1, [&](MapSerializer& mapSer) { + mapSer.WriteEntry("k", + [&](ShapeSerializer& vser) { vser.WriteBigDecimal(*decVal, MakeBigDecimal("-12345678901234567890.123456789")); }); + }); + }); + + JsonShapeDeserializer d(CursorOf(payload)); + Aws::Vector intValues; + Aws::Map decValues; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { + if (m.GetMemberName() == "ints") { + de.ReadList(m, [&](ShapeDeserializer& ede) { + auto v = ede.ReadBigInteger(*intElem); + ASSERT_TRUE(v.has_value()); + intValues.emplace_back(v.value().ToString().c_str()); + }); + } else if (m.GetMemberName() == "decs") { + de.ReadMap(m, [&](const Aws::String& key, ShapeDeserializer& vde) { + auto v = vde.ReadBigDecimal(*decVal); + ASSERT_TRUE(v.has_value()); + decValues[key] = v.value().ToString().c_str(); + }); + } + }); + ASSERT_EQ(intValues.size(), 2u); + EXPECT_EQ(intValues[0], "123456789012345678901234567890"); + EXPECT_EQ(intValues[1], "-7"); + ASSERT_EQ(decValues.size(), 1u); + EXPECT_EQ(decValues["k"], "-12345678901234567890123456789e-9"); +} + +TEST_F(JsonShapeDeserializerTest, BigIntegerRejectsMalformedInput) { + auto scalar = Schema::CreateBigInteger("BI"); + const char* const inputs[] = {"\"abc\"", "\"123\"", "1.5", "true", "[1]", "{}", "-", "1e3"}; + for (const char* input : inputs) { + SCOPED_TRACE(input); + JsonShapeDeserializer d(CursorOf(input)); + EXPECT_FALSE(d.ReadBigInteger(*scalar).has_value()); + } +} + +TEST_F(JsonShapeDeserializerTest, BigDecimalRejectsMalformedInput) { + auto scalar = Schema::CreateBigDecimal("BD"); + const char* const inputs[] = {"\"abc\"", "\"1.5\"", "true", "[1]", "{}", "-", "1.", "1e", "1.5.5"}; + for (const char* input : inputs) { + SCOPED_TRACE(input); + JsonShapeDeserializer d(CursorOf(input)); + EXPECT_FALSE(d.ReadBigDecimal(*scalar).has_value()); + } +} + +TEST_F(JsonShapeDeserializerTest, MalformedBigNumberDoesNotDerailFollowingMembers) { + auto root = Schema::StructureBuilder("Root") + .PutMember("a", Schema::CreateBigInteger("BI1")) + .PutMember("b", Schema::CreateBigInteger("BI2")) + .PutMember("c", Schema::CreateBigDecimal("BD")) + .PutMember("d", Schema::CreateBigInteger("BI3")) + .Build(); + const Aws::String payload = "{\"a\":\"abc\",\"b\":1.5,\"c\":\"x\",\"d\":123456789012345678901234567890}"; + JsonShapeDeserializer d(CursorOf(payload)); + Aws::Map parsed; + Aws::String dValue; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { + if (m.GetMemberName() == "c") { + parsed[m.GetMemberName()] = de.ReadBigDecimal(m).has_value(); + return; + } + auto v = de.ReadBigInteger(m); + parsed[m.GetMemberName()] = v.has_value(); + if (v.has_value() && m.GetMemberName() == "d") { + dValue = v.value().ToString().c_str(); + } + }); + EXPECT_FALSE(parsed.at("a")); + EXPECT_FALSE(parsed.at("b")); + EXPECT_FALSE(parsed.at("c")); + EXPECT_TRUE(parsed.at("d")); + EXPECT_EQ(dValue, "123456789012345678901234567890"); +} + +TEST_F(JsonShapeDeserializerTest, ByteAndShortRanges) { + auto root = Schema::StructureBuilder("Root") + .PutMember("b", Schema::CreateByte("B")) + .PutMember("s", Schema::CreateShort("S")) + .PutMember("after", Schema::CreateInteger("I")) + .Build(); + struct Case { + const char* json; + bool byteOk; + int byteValue; + bool shortOk; + int shortValue; + }; + const Case cases[] = { + {"{\"b\":-128,\"s\":-32768,\"after\":1}", true, -128, true, -32768}, + {"{\"b\":127,\"s\":32767,\"after\":1}", true, 127, true, 32767}, + {"{\"b\":128,\"s\":32768,\"after\":1}", false, 0, false, 0}, + {"{\"b\":-129,\"s\":-32769,\"after\":1}", false, 0, false, 0}, + }; + for (const auto& c : cases) { + SCOPED_TRACE(c.json); + JsonShapeDeserializer d(CursorOf(c.json)); + Aws::Crt::Optional b; + Aws::Crt::Optional s; + Aws::Crt::Optional after; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { + if (m.GetMemberName() == "b") { + b = de.ReadByte(m); + } else if (m.GetMemberName() == "s") { + s = de.ReadShort(m); + } else { + after = de.ReadInteger(m); + } + }); + ASSERT_EQ(b.has_value(), c.byteOk); + ASSERT_EQ(s.has_value(), c.shortOk); + if (c.byteOk) EXPECT_EQ(b.value(), c.byteValue); + if (c.shortOk) EXPECT_EQ(s.value(), c.shortValue); + // An out-of-range value is consumed, so the next member still reads. + ASSERT_TRUE(after.has_value()); + EXPECT_EQ(after.value(), 1); + } +} diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeSerializerTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeSerializerTest.cpp index fdda488e22b..1932b0d63bf 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeSerializerTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeSerializerTest.cpp @@ -583,3 +583,104 @@ TEST_F(JsonShapeSerializerTest, DocumentAsListElement) { ASSERT_TRUE(outcome.IsSuccess()); EXPECT_EQ(outcome.GetResult(), "[\"x\",true]"); } + +TEST_F(JsonShapeSerializerTest, BigIntegerBeyondInt64) { + JsonShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto member = Schema::CreateMember("big", ShapeType::BigInteger); + auto value = MakeBigInteger("123456789012345678901234567890"); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { ser.WriteBigInteger(*member, value); }); + s.WriteStruct(*root, rootStruct); + auto outcome = s.GetPayload(); + ASSERT_TRUE(outcome.IsSuccess()); + EXPECT_EQ(outcome.GetResult(), "{\"big\":123456789012345678901234567890}"); +} + +TEST_F(JsonShapeSerializerTest, BigDecimalSimple) { + JsonShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto member = Schema::CreateMember("dec", ShapeType::BigDecimal); + auto value = MakeBigDecimal("1.5"); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { ser.WriteBigDecimal(*member, value); }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), "{\"dec\":15e-1}"); +} + +TEST_F(JsonShapeSerializerTest, BigNumberJsonNameOverridesMemberName) { + JsonShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto member = + Schema::CreateMember("big", ShapeType::BigInteger, {{JsonNameTrait::KEY(), Aws::MakeShared("Schema", "BIG")}}); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { ser.WriteBigInteger(*member, MakeBigInteger("7")); }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), "{\"BIG\":7}"); +} + +TEST_F(JsonShapeSerializerTest, ListOfBigNumbers) { + JsonShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto ints = Schema::CreateMember("ints", ShapeType::List); + auto decs = Schema::CreateMember("decs", ShapeType::List); + auto intElem = Schema::CreateMember("member", ShapeType::BigInteger); + auto decElem = Schema::CreateMember("member", ShapeType::BigDecimal); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteList(*ints, 3, [&](ShapeSerializer& lser) { + lser.WriteBigInteger(*intElem, MakeBigInteger("1")); + lser.WriteBigInteger(*intElem, MakeBigInteger("123456789012345678901234567890")); + lser.WriteBigInteger(*intElem, MakeBigInteger("-123456789012345678901234567890")); + }); + ser.WriteList(*decs, 2, [&](ShapeSerializer& lser) { + lser.WriteBigDecimal(*decElem, MakeBigDecimal("1.5")); + lser.WriteBigDecimal(*decElem, MakeBigDecimal("-0.25")); + }); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), + "{\"ints\":[1,123456789012345678901234567890,-123456789012345678901234567890],\"decs\":[15e-1,-25e-2]}"); +} + +TEST_F(JsonShapeSerializerTest, MapOfBigNumbers) { + JsonShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto ints = Schema::CreateMember("ints", ShapeType::Map); + auto decs = Schema::CreateMember("decs", ShapeType::Map); + auto intVal = Schema::CreateMember("value", ShapeType::BigInteger); + auto decVal = Schema::CreateMember("value", ShapeType::BigDecimal); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteMap(*ints, 2, [&](MapSerializer& mapSer) { + mapSer.WriteEntry("a", + [&](ShapeSerializer& vser) { vser.WriteBigInteger(*intVal, MakeBigInteger("123456789012345678901234567890")); }); + mapSer.WriteEntry("b", [&](ShapeSerializer& vser) { vser.WriteBigInteger(*intVal, MakeBigInteger("0")); }); + }); + ser.WriteMap(*decs, 1, [&](MapSerializer& mapSer) { + mapSer.WriteEntry("c", [&](ShapeSerializer& vser) { vser.WriteBigDecimal(*decVal, MakeBigDecimal("0.100000000000000000000001")); }); + }); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), + "{\"ints\":{\"a\":123456789012345678901234567890,\"b\":0},\"decs\":{\"c\":100000000000000000000001e-24}}"); +} + +TEST_F(JsonShapeSerializerTest, BigNumberAsRootValue) { + JsonShapeSerializer s; + auto schema = Schema::CreateBigInteger("Big"); + s.WriteBigInteger(*schema, MakeBigInteger("123456789012345678901234567890")); + EXPECT_EQ(s.GetPayload().GetResult(), "123456789012345678901234567890"); +} + +TEST_F(JsonShapeSerializerTest, ByteAndShortAreNumbers) { + JsonShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto byteMin = Schema::CreateMember("byteMin", ShapeType::Byte); + auto byteMax = Schema::CreateMember("byteMax", ShapeType::Byte); + auto shortMin = Schema::CreateMember("shortMin", ShapeType::Short); + auto shortMax = Schema::CreateMember("shortMax", ShapeType::Short); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteByte(*byteMin, -128); + ser.WriteByte(*byteMax, 65); + ser.WriteShort(*shortMin, -32768); + ser.WriteShort(*shortMax, 32767); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), "{\"byteMin\":-128,\"byteMax\":65,\"shortMin\":-32768,\"shortMax\":32767}"); +} diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/QueryShapeSerializerTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/QueryShapeSerializerTest.cpp index 07ecc28525c..78b59d6e66d 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/QueryShapeSerializerTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/QueryShapeSerializerTest.cpp @@ -340,3 +340,125 @@ TEST_F(QueryShapeSerializerTest, DocumentIsUnsupported) { ASSERT_FALSE(outcome.IsSuccess()); EXPECT_EQ(outcome.GetError().GetExceptionName(), "SerializationException"); } + +TEST_F(QueryShapeSerializerTest, BigIntegerBeyondInt64) { + QueryShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto member = Schema::CreateMember("big", ShapeType::BigInteger); + auto value = MakeBigInteger("123456789012345678901234567890"); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { ser.WriteBigInteger(*member, value); }); + s.WriteStruct(*root, rootStruct); + auto outcome = s.GetPayload(); + ASSERT_TRUE(outcome.IsSuccess()); + EXPECT_EQ(outcome.GetResult(), "big=123456789012345678901234567890"); +} + +TEST_F(QueryShapeSerializerTest, BigDecimalValues) { + QueryShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto simple = Schema::CreateMember("simple", ShapeType::BigDecimal); + auto small = Schema::CreateMember("small", ShapeType::BigDecimal); + auto negative = Schema::CreateMember("negative", ShapeType::BigDecimal); + auto zero = Schema::CreateMember("zero", ShapeType::BigDecimal); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteBigDecimal(*simple, MakeBigDecimal("1.5")); + ser.WriteBigDecimal(*small, MakeBigDecimal("0.100000000000000000000001")); + ser.WriteBigDecimal(*negative, MakeBigDecimal("-12345678901234567890.123456789")); + ser.WriteBigDecimal(*zero, Aws::Crt::BigDecimal()); + }); + s.WriteStruct(*root, rootStruct); + auto outcome = s.GetPayload(); + ASSERT_TRUE(outcome.IsSuccess()); + EXPECT_EQ(outcome.GetResult(), "simple=15e-1&small=100000000000000000000001e-24&negative=-12345678901234567890123456789e-9&zero=0e0"); +} + +TEST_F(QueryShapeSerializerTest, BigNumberXmlNameOverridesMemberName) { + QueryShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto member = Schema::CreateMember("internalName", ShapeType::BigInteger, + {{XmlNameTrait::KEY(), Aws::MakeShared("Schema", "WireName")}}); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { ser.WriteBigInteger(*member, MakeBigInteger("5")); }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), "WireName=5"); +} + +TEST_F(QueryShapeSerializerTest, ListOfBigNumbers) { + QueryShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto ints = Schema::CreateMember("ints", ShapeType::List); + auto decs = Schema::CreateMember("decs", ShapeType::List); + auto intElem = Schema::CreateMember("member", ShapeType::BigInteger); + auto decElem = Schema::CreateMember("member", ShapeType::BigDecimal); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteList(*ints, 2, [&](ShapeSerializer& lser) { + lser.WriteBigInteger(*intElem, MakeBigInteger("123456789012345678901234567890")); + lser.WriteBigInteger(*intElem, MakeBigInteger("-1")); + }); + ser.WriteList(*decs, 1, [&](ShapeSerializer& lser) { lser.WriteBigDecimal(*decElem, MakeBigDecimal("1.5")); }); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), "ints.member.1=123456789012345678901234567890&ints.member.2=-1&decs.member.1=15e-1"); +} + +TEST_F(QueryShapeSerializerTest, FlattenedListOfBigIntegers) { + QueryShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto list = Schema::CreateMember("nums", ShapeType::List, {{XmlFlattenedTrait::KEY(), Aws::MakeShared("Schema")}}); + auto elem = Schema::CreateMember("member", ShapeType::BigInteger); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteList(*list, 2, [&](ShapeSerializer& lser) { + lser.WriteBigInteger(*elem, MakeBigInteger("1")); + lser.WriteBigInteger(*elem, MakeBigInteger("2")); + }); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), "nums.1=1&nums.2=2"); +} + +TEST_F(QueryShapeSerializerTest, MapOfBigNumbers) { + QueryShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto ints = Schema::CreateMember("ints", ShapeType::Map); + auto decs = Schema::CreateMember("decs", ShapeType::Map); + auto intVal = Schema::CreateMember("value", ShapeType::BigInteger); + auto decVal = Schema::CreateMember("value", ShapeType::BigDecimal); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteMap(*ints, 1, [&](MapSerializer& mapSer) { + mapSer.WriteEntry("a", + [&](ShapeSerializer& vser) { vser.WriteBigInteger(*intVal, MakeBigInteger("123456789012345678901234567890")); }); + }); + ser.WriteMap(*decs, 1, [&](MapSerializer& mapSer) { + mapSer.WriteEntry("b", [&](ShapeSerializer& vser) { vser.WriteBigDecimal(*decVal, MakeBigDecimal("0.100000000000000000000001")); }); + }); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), + "ints.entry.1.key=a&ints.entry.1.value=123456789012345678901234567890" + "&decs.entry.1.key=b&decs.entry.1.value=100000000000000000000001e-24"); +} + +TEST_F(QueryShapeSerializerTest, Ec2BigNumberCapitalizesMemberName) { + QueryShapeSerializer s(QueryShapeSerializer::Flavor::Ec2Query); + auto root = Schema::StructureBuilder("Root").Build(); + auto big = Schema::CreateMember("bigValue", ShapeType::BigInteger); + auto dec = Schema::CreateMember("decValue", ShapeType::BigDecimal); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteBigInteger(*big, MakeBigInteger("123456789012345678901234567890")); + ser.WriteBigDecimal(*dec, MakeBigDecimal("1.5")); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), "BigValue=123456789012345678901234567890&DecValue=15e-1"); +} + +TEST_F(QueryShapeSerializerTest, ByteAndShortAreNumbers) { + QueryShapeSerializer s; + auto root = Schema::StructureBuilder("Root").Build(); + auto b = Schema::CreateMember("b", ShapeType::Byte); + auto sh = Schema::CreateMember("s", ShapeType::Short); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteByte(*b, 65); + ser.WriteShort(*sh, -32768); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), "b=65&s=-32768"); +} diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/SchemaSerializerTestHelpers.h b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/SchemaSerializerTestHelpers.h index 29774bc3044..b36a6453edd 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/SchemaSerializerTestHelpers.h +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/SchemaSerializerTestHelpers.h @@ -4,6 +4,9 @@ */ #pragma once +#include +#include +#include #include #include #include @@ -36,5 +39,17 @@ class LambdaStruct final : public SerializableStruct { std::function m_members; }; +// Parses a decimal literal for tests; yields zero on a malformed literal so the +// comparison against the expected text fails instead of crashing. +inline Aws::Crt::BigInteger MakeBigInteger(const char* text) { + auto parsed = Aws::Crt::BigInteger::FromString(Aws::Crt::ByteCursorFromCString(text)); + return parsed.has_value() ? parsed.value() : Aws::Crt::BigInteger(); +} + +inline Aws::Crt::BigDecimal MakeBigDecimal(const char* text) { + auto parsed = Aws::Crt::BigDecimal::FromString(Aws::Crt::ByteCursorFromCString(text)); + return parsed.has_value() ? parsed.value() : Aws::Crt::BigDecimal(); +} + } // namespace schema } // namespace smithy diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/XmlShapeDeserializerTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/XmlShapeDeserializerTest.cpp index 05d80bc3df8..384dfd056ed 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/XmlShapeDeserializerTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/XmlShapeDeserializerTest.cpp @@ -656,3 +656,212 @@ TEST_F(XmlShapeDeserializerTest, ReadDocumentIsUnsupported) { auto schema = Schema::CreateDocument("smithy.api#Document"); EXPECT_EQ(deser.ReadDocument(*schema), nullptr); } + +namespace { + +Aws::Crt::ByteCursor CursorOf(const Aws::String& payload) { + return Aws::Crt::ByteCursorFromArray(reinterpret_cast(payload.data()), payload.size()); +} + +} // namespace + +TEST_F(XmlShapeDeserializerTest, BigIntegerValues) { + auto root = RootBuilder().PutMember("big", Schema::CreateBigInteger("BI")).Build(); + struct Case { + const char* text; + const char* expected; + }; + const Case cases[] = { + {"0", "0"}, + {"42", "42"}, + {"-42", "-42"}, + {"18446744073709551616", "18446744073709551616"}, + {"123456789012345678901234567890", "123456789012345678901234567890"}, + }; + for (const auto& c : cases) { + SCOPED_TRACE(c.text); + const Aws::String payload = Aws::String("") + c.text + ""; + XmlShapeDeserializer d(CursorOf(payload)); + Aws::Crt::Optional got; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { got = de.ReadBigInteger(m); }); + ASSERT_TRUE(got.has_value()); + EXPECT_EQ(got.value().ToString(), c.expected); + } +} + +TEST_F(XmlShapeDeserializerTest, BigDecimalValues) { + auto root = RootBuilder().PutMember("dec", Schema::CreateBigDecimal("BD")).Build(); + struct Case { + const char* text; + const char* expected; + }; + const Case cases[] = { + {"1.5", "15e-1"}, + {"15e-1", "15e-1"}, + {"-0.25", "-25e-2"}, + {"123456789012345678901234567890.5", "1234567890123456789012345678905e-1"}, + }; + for (const auto& c : cases) { + SCOPED_TRACE(c.text); + const Aws::String payload = Aws::String("") + c.text + ""; + XmlShapeDeserializer d(CursorOf(payload)); + Aws::Crt::Optional got; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { got = de.ReadBigDecimal(m); }); + ASSERT_TRUE(got.has_value()); + EXPECT_EQ(got.value().ToString(), c.expected); + } +} + +TEST_F(XmlShapeDeserializerTest, BigNumberAttributes) { + auto root = + RootBuilder() + .PutMember("id", Schema::CreateBigInteger("BI"), {{XmlAttributeTrait::KEY(), Aws::MakeShared("Test")}}) + .PutMember("scale", Schema::CreateBigDecimal("BD"), {{XmlAttributeTrait::KEY(), Aws::MakeShared("Test")}}) + .Build(); + auto id = root->GetMember("id").value(); + auto scale = root->GetMember("scale").value(); + auto payload = Encode(root, [&](ShapeSerializer& ser) { + ser.WriteBigInteger(*id, MakeBigInteger("123456789012345678901234567890")); + ser.WriteBigDecimal(*scale, MakeBigDecimal("-0.25")); + }); + EXPECT_NE(payload.find("id=\"123456789012345678901234567890\""), Aws::String::npos); + + XmlShapeDeserializer d(CursorOf(payload)); + Aws::String gotId, gotScale; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { + if (m.GetMemberName() == "id") { + auto v = de.ReadBigInteger(m); + ASSERT_TRUE(v.has_value()); + gotId = v.value().ToString().c_str(); + } else { + auto v = de.ReadBigDecimal(m); + ASSERT_TRUE(v.has_value()); + gotScale = v.value().ToString().c_str(); + } + }); + EXPECT_EQ(gotId, "123456789012345678901234567890"); + EXPECT_EQ(gotScale, "-25e-2"); +} + +TEST_F(XmlShapeDeserializerTest, BigNumbersInListAndMap) { + auto intsBuilder = Schema::ListBuilder("Ints"); + auto decsBuilder = Schema::MapBuilder("Decs"); + auto root = RootBuilder().PutMember("ints", intsBuilder).PutMember("decs", decsBuilder).Build(); + auto ints = root->GetMember("ints").value(); + auto decs = root->GetMember("decs").value(); + auto intElem = Schema::CreateMember("member", ShapeType::BigInteger); + auto decVal = Schema::CreateMember("value", ShapeType::BigDecimal); + auto payload = Encode(root, [&](ShapeSerializer& ser) { + ser.WriteList(*ints, 2, [&](ShapeSerializer& lser) { + lser.WriteBigInteger(*intElem, MakeBigInteger("123456789012345678901234567890")); + lser.WriteBigInteger(*intElem, MakeBigInteger("-7")); + }); + ser.WriteMap(*decs, 1, [&](MapSerializer& mapSer) { + mapSer.WriteEntry("k", + [&](ShapeSerializer& vser) { vser.WriteBigDecimal(*decVal, MakeBigDecimal("-12345678901234567890.123456789")); }); + }); + }); + + XmlShapeDeserializer d(CursorOf(payload)); + Aws::Vector intValues; + Aws::Map decValues; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { + if (m.GetMemberName() == "ints") { + de.ReadList(m, [&](ShapeDeserializer& ede) { + auto v = ede.ReadBigInteger(*intElem); + ASSERT_TRUE(v.has_value()); + intValues.emplace_back(v.value().ToString().c_str()); + }); + } else if (m.GetMemberName() == "decs") { + de.ReadMap(m, [&](const Aws::String& key, ShapeDeserializer& vde) { + auto v = vde.ReadBigDecimal(*decVal); + ASSERT_TRUE(v.has_value()); + decValues[key] = v.value().ToString().c_str(); + }); + } + }); + ASSERT_EQ(intValues.size(), 2u); + EXPECT_EQ(intValues[0], "123456789012345678901234567890"); + EXPECT_EQ(intValues[1], "-7"); + ASSERT_EQ(decValues.size(), 1u); + EXPECT_EQ(decValues["k"], "-12345678901234567890123456789e-9"); +} + +TEST_F(XmlShapeDeserializerTest, BigIntegerRejectsMalformedText) { + auto root = RootBuilder().PutMember("big", Schema::CreateBigInteger("BI")).Build(); + const char* const inputs[] = {"12x", "abc", "1.5", "", " ", "1e3", "--1", "0x10"}; + for (const char* input : inputs) { + SCOPED_TRACE(input); + const Aws::String payload = Aws::String("") + input + ""; + XmlShapeDeserializer d(CursorOf(payload)); + bool delivered = false; + Aws::Crt::Optional got; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { + delivered = true; + got = de.ReadBigInteger(m); + }); + EXPECT_TRUE(delivered); + EXPECT_FALSE(got.has_value()); + } +} + +TEST_F(XmlShapeDeserializerTest, BigDecimalRejectsMalformedText) { + auto root = RootBuilder().PutMember("dec", Schema::CreateBigDecimal("BD")).Build(); + const char* const inputs[] = {"12x", "abc", "", " ", "1.", "1e", "1.5.5", "NaN"}; + for (const char* input : inputs) { + SCOPED_TRACE(input); + const Aws::String payload = Aws::String("") + input + ""; + XmlShapeDeserializer d(CursorOf(payload)); + bool delivered = false; + Aws::Crt::Optional got; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { + delivered = true; + got = de.ReadBigDecimal(m); + }); + EXPECT_TRUE(delivered); + EXPECT_FALSE(got.has_value()); + } +} + +TEST_F(XmlShapeDeserializerTest, ByteAndShortRanges) { + auto root = RootBuilder() + .PutMember("b", Schema::CreateByte("B")) + .PutMember("s", Schema::CreateShort("S")) + .PutMember("after", Schema::CreateInteger("I")) + .Build(); + struct Case { + const char* xml; + bool byteOk; + int byteValue; + bool shortOk; + int shortValue; + }; + const Case cases[] = { + {"-128-327681", true, -128, true, -32768}, + {"127327671", true, 127, true, 32767}, + {"128327681", false, 0, false, 0}, + {"-129-327691", false, 0, false, 0}, + }; + for (const auto& c : cases) { + SCOPED_TRACE(c.xml); + XmlShapeDeserializer d(CursorOf(c.xml)); + Aws::Crt::Optional b; + Aws::Crt::Optional s; + Aws::Crt::Optional after; + d.ReadStruct(*root, [&](const Schema& m, ShapeDeserializer& de) { + if (m.GetMemberName() == "b") { + b = de.ReadByte(m); + } else if (m.GetMemberName() == "s") { + s = de.ReadShort(m); + } else { + after = de.ReadInteger(m); + } + }); + ASSERT_EQ(b.has_value(), c.byteOk); + ASSERT_EQ(s.has_value(), c.shortOk); + if (c.byteOk) EXPECT_EQ(b.value(), c.byteValue); + if (c.shortOk) EXPECT_EQ(s.value(), c.shortValue); + ASSERT_TRUE(after.has_value()); + EXPECT_EQ(after.value(), 1); + } +} diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/XmlShapeSerializerTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/XmlShapeSerializerTest.cpp index 96b03411ec1..1e598e5dc32 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/XmlShapeSerializerTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/XmlShapeSerializerTest.cpp @@ -534,3 +534,132 @@ TEST_F(XmlShapeSerializerTest, DocumentIsUnsupported) { ASSERT_FALSE(outcome.IsSuccess()); EXPECT_EQ(outcome.GetError().GetExceptionName(), "SerializationException"); } + +TEST_F(XmlShapeSerializerTest, BigIntegerBeyondInt64) { + XmlShapeSerializer s; + auto root = Schema::StructureBuilder("Root", {{XmlNameTrait::KEY(), Aws::MakeShared("Schema", "Root")}}).Build(); + auto member = Schema::CreateMember("big", ShapeType::BigInteger); + auto value = MakeBigInteger("123456789012345678901234567890"); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { ser.WriteBigInteger(*member, value); }); + s.WriteStruct(*root, rootStruct); + auto outcome = s.GetPayload(); + ASSERT_TRUE(outcome.IsSuccess()); + EXPECT_EQ(outcome.GetResult(), "123456789012345678901234567890"); +} + +TEST_F(XmlShapeSerializerTest, BigDecimalValues) { + XmlShapeSerializer s; + auto root = Schema::StructureBuilder("Root", {{XmlNameTrait::KEY(), Aws::MakeShared("Schema", "Root")}}).Build(); + auto simple = Schema::CreateMember("simple", ShapeType::BigDecimal); + auto small = Schema::CreateMember("small", ShapeType::BigDecimal); + auto negative = Schema::CreateMember("negative", ShapeType::BigDecimal); + auto zero = Schema::CreateMember("zero", ShapeType::BigDecimal); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteBigDecimal(*simple, MakeBigDecimal("1.5")); + ser.WriteBigDecimal(*small, MakeBigDecimal("0.100000000000000000000001")); + ser.WriteBigDecimal(*negative, MakeBigDecimal("-12345678901234567890.123456789")); + ser.WriteBigDecimal(*zero, Aws::Crt::BigDecimal()); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), + "15e-1100000000000000000000001e-24" + "-12345678901234567890123456789e-90e0"); +} + +TEST_F(XmlShapeSerializerTest, BigNumberXmlNameOverridesMemberName) { + XmlShapeSerializer s; + auto root = Schema::StructureBuilder("Root", {{XmlNameTrait::KEY(), Aws::MakeShared("Schema", "Root")}}).Build(); + auto member = Schema::CreateMember("big", ShapeType::BigInteger, {{XmlNameTrait::KEY(), Aws::MakeShared("Schema", "Big")}}); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { ser.WriteBigInteger(*member, MakeBigInteger("5")); }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), "5"); +} + +TEST_F(XmlShapeSerializerTest, BigNumberAttributes) { + XmlShapeSerializer s; + auto root = Schema::StructureBuilder("Root", {{XmlNameTrait::KEY(), Aws::MakeShared("Schema", "Root")}}).Build(); + auto intAttr = + Schema::CreateMember("id", ShapeType::BigInteger, {{XmlAttributeTrait::KEY(), Aws::MakeShared("Schema")}}); + auto decAttr = + Schema::CreateMember("scale", ShapeType::BigDecimal, {{XmlAttributeTrait::KEY(), Aws::MakeShared("Schema")}}); + auto element = Schema::CreateMember("name", ShapeType::String); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteBigInteger(*intAttr, MakeBigInteger("123456789012345678901234567890")); + ser.WriteBigDecimal(*decAttr, MakeBigDecimal("-0.25")); + ser.WriteString(*element, "x"); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), "x"); +} + +TEST_F(XmlShapeSerializerTest, ListOfBigNumbers) { + XmlShapeSerializer s; + auto root = Schema::StructureBuilder("Root", {{XmlNameTrait::KEY(), Aws::MakeShared("Schema", "Root")}}).Build(); + auto ints = Schema::CreateMember("ints", ShapeType::List); + auto decs = Schema::CreateMember("decs", ShapeType::List); + auto intElem = Schema::CreateMember("member", ShapeType::BigInteger); + auto decElem = Schema::CreateMember("member", ShapeType::BigDecimal); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteList(*ints, 2, [&](ShapeSerializer& lser) { + lser.WriteBigInteger(*intElem, MakeBigInteger("123456789012345678901234567890")); + lser.WriteBigInteger(*intElem, MakeBigInteger("-1")); + }); + ser.WriteList(*decs, 1, [&](ShapeSerializer& lser) { lser.WriteBigDecimal(*decElem, MakeBigDecimal("1.5")); }); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), + "123456789012345678901234567890-1" + "15e-1"); +} + +TEST_F(XmlShapeSerializerTest, FlattenedListOfBigIntegers) { + XmlShapeSerializer s; + auto root = Schema::StructureBuilder("Root", {{XmlNameTrait::KEY(), Aws::MakeShared("Schema", "Root")}}).Build(); + auto list = Schema::CreateMember("nums", ShapeType::List, {{XmlFlattenedTrait::KEY(), Aws::MakeShared("Schema")}}); + auto elem = Schema::CreateMember("member", ShapeType::BigInteger); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteList(*list, 2, [&](ShapeSerializer& lser) { + lser.WriteBigInteger(*elem, MakeBigInteger("1")); + lser.WriteBigInteger(*elem, MakeBigInteger("2")); + }); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), "12"); +} + +TEST_F(XmlShapeSerializerTest, MapOfBigNumbers) { + XmlShapeSerializer s; + auto root = Schema::StructureBuilder("Root", {{XmlNameTrait::KEY(), Aws::MakeShared("Schema", "Root")}}).Build(); + auto ints = Schema::CreateMember("ints", ShapeType::Map); + auto decs = Schema::CreateMember("decs", ShapeType::Map); + auto intVal = Schema::CreateMember("value", ShapeType::BigInteger); + auto decVal = Schema::CreateMember("value", ShapeType::BigDecimal); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteMap(*ints, 1, [&](MapSerializer& mapSer) { + mapSer.WriteEntry("a", + [&](ShapeSerializer& vser) { vser.WriteBigInteger(*intVal, MakeBigInteger("123456789012345678901234567890")); }); + }); + ser.WriteMap(*decs, 1, [&](MapSerializer& mapSer) { + mapSer.WriteEntry("b", [&](ShapeSerializer& vser) { vser.WriteBigDecimal(*decVal, MakeBigDecimal("0.100000000000000000000001")); }); + }); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), + "a123456789012345678901234567890" + "b100000000000000000000001e-24"); +} + +TEST_F(XmlShapeSerializerTest, ByteAndShortAreNumbers) { + XmlShapeSerializer s; + auto root = Schema::StructureBuilder("Root", {{XmlNameTrait::KEY(), Aws::MakeShared("Schema", "Root")}}).Build(); + auto attr = Schema::CreateMember("b", ShapeType::Byte, {{XmlAttributeTrait::KEY(), Aws::MakeShared("Schema")}}); + auto element = Schema::CreateMember("e", ShapeType::Byte); + auto sh = Schema::CreateMember("s", ShapeType::Short); + LambdaStruct rootStruct(*root, [&](ShapeSerializer& ser) { + ser.WriteByte(*attr, 65); + ser.WriteByte(*element, -128); + ser.WriteShort(*sh, 32767); + }); + s.WriteStruct(*root, rootStruct); + EXPECT_EQ(s.GetPayload().GetResult(), "-12832767"); +} diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/CppTypeMapper.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/CppTypeMapper.java index 19259e26f51..77b2fc4f830 100644 --- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/CppTypeMapper.java +++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/main/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/CppTypeMapper.java @@ -84,6 +84,12 @@ public static String getCppType(Shape shape, Model model, boolean wideIntegers) if (shape.isLongShape()) { return "long long"; } + if (shape.isByteShape()) { + return "int8_t"; + } + if (shape.isShortShape()) { + return "int16_t"; + } if (shape.isBooleanShape()) { return "bool"; } @@ -120,6 +126,12 @@ public static String getCppType(Shape shape, Model model, boolean wideIntegers) if (shape.isDocumentShape()) { return "Aws::Utils::Document"; } + if (shape.isBigIntegerShape()) { + return "Aws::Crt::BigInteger"; + } + if (shape.isBigDecimalShape()) { + return "Aws::Crt::BigDecimal"; + } throw new UnsupportedOperationException( "Unsupported shape type " + shape.getType() + " for shape " + shape.getId()); } @@ -149,7 +161,7 @@ public static Optional getDefaultValue(Shape shape) { if (isEnum(shape)) { return Optional.of(cppShapeName(shape) + "::NOT_SET"); } - if (shape.isIntegerShape() || shape.isLongShape()) { + if (shape.isIntegerShape() || shape.isLongShape() || shape.isByteShape() || shape.isShortShape()) { return Optional.of("0"); } if (shape.isBooleanShape()) { @@ -158,20 +170,32 @@ public static Optional getDefaultValue(Shape shape) { if (shape.isDoubleShape() || shape.isFloatShape()) { return Optional.of("0.0"); } - if (shape.isTimestampShape() || shape.isBlobShape()) { + if (shape.isTimestampShape() || shape.isBlobShape() || isArbitraryPrecision(shape)) { return Optional.of(""); // produces Type m_field{}; (value-initialization) } return Optional.empty(); } /** - * Returns true if the shape maps to a C++ primitive type (int, long long, bool, double). + * Returns true if the shape is a {@code bigInteger} or {@code bigDecimal}, which map to + * {@code Aws::Crt::BigInteger} / {@code Aws::Crt::BigDecimal}. Unlike the scalar primitives these + * are class types, so accessors take them by const reference. + * + * @param shape the shape to check + * @return true if the shape is an arbitrary-precision number + */ + public static boolean isArbitraryPrecision(Shape shape) { + return shape.isBigIntegerShape() || shape.isBigDecimalShape(); + } + + /** + * Returns true if the shape maps to a C++ primitive type (int8_t, int16_t, int, long long, bool, double). * * @param shape the shape to check * @return true if the shape is a primitive type */ public static boolean isPrimitive(Shape shape) { - return shape.isIntegerShape() || shape.isLongShape() + return shape.isIntegerShape() || shape.isLongShape() || shape.isByteShape() || shape.isShortShape() || shape.isBooleanShape() || shape.isDoubleShape() || shape.isFloatShape(); } @@ -192,21 +216,24 @@ public static boolean isEnum(Shape shape) { * *

Primitive types (int, long, bool, double, float) have meaningful default * values, so the SDK needs a separate boolean to distinguish "not set" from - * "set to the default value." + * "set to the default value." {@code bigInteger} and {@code bigDecimal} default-construct to + * zero, which is likewise indistinguishable from an explicitly set zero. * * @param shape the shape to check * @return true if a hasBeenSet flag is needed */ public static boolean needsHasBeenSetFlag(Shape shape) { - return shape.isIntegerShape() || shape.isLongShape() - || shape.isBooleanShape() || shape.isDoubleShape() || shape.isFloatShape(); + return shape.isIntegerShape() || shape.isLongShape() || shape.isByteShape() || shape.isShortShape() + || shape.isBooleanShape() || shape.isDoubleShape() || shape.isFloatShape() + || isArbitraryPrecision(shape); } /** * Returns the {@code #include} path for a shape used as a member type. * *

Structure and enum shapes from the same service produce relative model includes. - * Core types (string, blob, timestamp, etc.) produce aws/core/ includes. + * Core types (string, blob, timestamp, etc.) produce aws/core/ includes; bigInteger and + * bigDecimal produce the aws-crt-cpp headers. * Primitives (int, bool, double) need no include and return empty. * * @param shape the member's target shape @@ -228,6 +255,12 @@ public static Optional getIncludeForMemberType(Shape shape, Model model, if (shape.isTimestampShape()) { return Optional.of(""); } + if (shape.isBigIntegerShape()) { + return Optional.of(""); + } + if (shape.isBigDecimalShape()) { + return Optional.of(""); + } if (shape.isListShape()) { return Optional.of(""); } diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/BigNumberModelGeneratorTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/BigNumberModelGeneratorTest.java new file mode 100644 index 00000000000..2a29f8a058e --- /dev/null +++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/BigNumberModelGeneratorTest.java @@ -0,0 +1,93 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +package com.amazonaws.util.awsclientsmithygenerator.generators.model; + +import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriterDelegator; +import com.amazonaws.util.awsclientsmithygenerator.generators.model.transforms.GlobalTransforms; +import org.junit.jupiter.api.Test; +import software.amazon.smithy.build.MockManifest; +import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.ShapeId; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Drives {@link ModelGenerator} end-to-end over a restJson1 service whose members use + * {@code bigInteger} / {@code bigDecimal} as plain members, list and map elements, and HTTP-bound + * header, query and label members. + */ +class BigNumberModelGeneratorTest { + + static final String IDL = String.join("\n", + "$version: \"2.0\"", + "namespace com.example.big", + "use aws.api#service", + "use aws.protocols#restJson1", + "@service(sdkId: \"Big Svc\", arnNamespace: \"bigsvc\", cloudFormationName: \"BigSvc\",", + " cloudTrailEventSource: \"bigsvc.amazonaws.com\")", + "@restJson1", + "service BigService { version: \"2024-01-01\", operations: [PutThing] }", + "@http(method: \"POST\", uri: \"/things\")", + "operation PutThing { input: PutThingInput, output: PutThingOutput }", + "structure PutThingInput {", + " @required id: BigInteger", + " total: BigDecimal", + " ids: IntegerList", + " byName: DecimalMap", + " sparseIds: SparseIntegerList", + " sparseByName: SparseDecimalMap", + " nested: Nested", + "}", + "structure PutThingOutput {", + " total: BigDecimal", + "}", + "structure Nested { value: BigDecimal, ids: IntegerList }", + "list IntegerList { member: BigInteger }", + "list DecimalList { member: BigDecimal }", + "@sparse list SparseIntegerList { member: BigInteger }", + "map DecimalMap { key: String, value: BigDecimal }", + "@sparse map SparseDecimalMap { key: String, value: BigDecimal }"); + + static MockManifest generate() { + Model model = Model.assembler() + .discoverModels(BigNumberModelGeneratorTest.class.getClassLoader()) + .addUnparsedModel("big.smithy", IDL) + .assemble() + .unwrap(); + ServiceShape service = model.expectShape( + ShapeId.from("com.example.big#BigService"), ServiceShape.class); + Model transformed = new TransformPipeline(List.of(new GlobalTransforms())).apply(model, service); + MockManifest manifest = new MockManifest(); + CppWriterDelegator delegator = new CppWriterDelegator(manifest); + new ModelGenerator(transformed, service, delegator, "bigsvc", "AWS_BIGSVC_API", "BigSvc") + .generateAll(); + delegator.flushWriters(); + return manifest; + } + + private static String file(MockManifest manifest, String suffix) { + return manifest.getFileString(manifest.getFiles().stream() + .filter(p -> p.toString().endsWith(suffix)).findFirst().orElseThrow()).orElseThrow(); + } + + @Test + void request_rendersCrtTypesForEveryBigNumberPosition() { + String h = file(generate(), "PutThingRequest.h"); + assertTrue(h.contains("#include "), h); + assertTrue(h.contains("#include "), h); + assertTrue(h.contains("#include "), h); + assertTrue(h.contains("inline const Aws::Crt::BigInteger& GetId() const"), h); + assertTrue(h.contains("inline const Aws::Crt::BigDecimal& GetTotal() const"), h); + assertTrue(h.contains("inline const Aws::Vector& GetIds() const"), h); + assertTrue(h.contains("inline const Aws::Map& GetByName() const"), h); + assertTrue(h.contains( + "inline const Aws::Vector>& GetSparseIds() const"), h); + assertTrue(h.contains( + "inline const Aws::Map>& GetSparseByName() const"), h); + } +} diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/CppTypeMapperTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/CppTypeMapperTest.java index 9232e21bf08..75e2b5b7055 100644 --- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/CppTypeMapperTest.java +++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/CppTypeMapperTest.java @@ -6,6 +6,10 @@ import org.junit.jupiter.api.Test; import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.BigDecimalShape; +import software.amazon.smithy.model.shapes.BigIntegerShape; +import software.amazon.smithy.model.shapes.ByteShape; +import software.amazon.smithy.model.shapes.ShortShape; import software.amazon.smithy.model.shapes.BlobShape; import software.amazon.smithy.model.shapes.BooleanShape; import software.amazon.smithy.model.shapes.DocumentShape; @@ -23,6 +27,7 @@ import software.amazon.smithy.model.shapes.UnionShape; import java.util.List; +import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; @@ -792,4 +797,111 @@ void getIncludesForShape_singleLevelListOfStruct_unchanged() { "", ""), includes); } + + // --- bigInteger / bigDecimal tests --- + + private static ListShape listOf(String id, Shape element) { + return ListShape.builder().id(id) + .member(MemberShape.builder().id(id + "$member").target(element.getId()).build()).build(); + } + + @Test + void bigIntegerShape_mapsToCrtBigInteger() { + BigIntegerShape shape = BigIntegerShape.builder().id("com.example#Big").build(); + Model model = Model.builder().addShape(shape).build(); + assertEquals("Aws::Crt::BigInteger", CppTypeMapper.getCppType(shape, model)); + } + + @Test + void bigNumbers_wideIntegers_areNotWidened() { + BigIntegerShape bi = BigIntegerShape.builder().id("com.example#Big").build(); + BigDecimalShape bd = BigDecimalShape.builder().id("com.example#Dec").build(); + Model model = Model.builder().addShapes(bi, bd).build(); + assertEquals("Aws::Crt::BigInteger", CppTypeMapper.getCppType(bi, model, true)); + assertEquals("Aws::Crt::BigDecimal", CppTypeMapper.getCppType(bd, model, true)); + } + + @Test + void sparseListOfBigDecimal_wrapsElementInOptional() { + BigDecimalShape bd = BigDecimalShape.builder().id("com.example#Dec").build(); + ListShape list = ListShape.builder().id("com.example#DecList") + .member(MemberShape.builder().id("com.example#DecList$member").target(bd.getId()).build()) + .addTrait(new software.amazon.smithy.model.traits.SparseTrait()).build(); + Model model = Model.builder().addShapes(bd, list).build(); + assertEquals("Aws::Vector>", + CppTypeMapper.getCppType(list, model)); + } + + @Test + void defaultValue_bigNumbers_isValueInitialized() { + // Same precedent as blob / timestamp: Type m_field{}; (zero-valued default construction). + assertEquals(java.util.Optional.of(""), + CppTypeMapper.getDefaultValue(BigIntegerShape.builder().id("com.example#Big").build())); + assertEquals(java.util.Optional.of(""), + CppTypeMapper.getDefaultValue(BigDecimalShape.builder().id("com.example#Dec").build())); + } + + @Test + void bigNumbers_areNotPrimitive_butNeedHasBeenSetFlag() { + BigIntegerShape bi = BigIntegerShape.builder().id("com.example#Big").build(); + BigDecimalShape bd = BigDecimalShape.builder().id("com.example#Dec").build(); + assertFalse(CppTypeMapper.isPrimitive(bi)); + assertFalse(CppTypeMapper.isPrimitive(bd)); + assertTrue(CppTypeMapper.needsHasBeenSetFlag(bi)); + assertTrue(CppTypeMapper.needsHasBeenSetFlag(bd)); + } + + @Test + void includeForBigNumbers_isCrtHeader() { + BigIntegerShape bi = BigIntegerShape.builder().id("com.example#Big").build(); + BigDecimalShape bd = BigDecimalShape.builder().id("com.example#Dec").build(); + Model model = Model.builder().addShapes(bi, bd).build(); + assertEquals(java.util.Optional.of(""), + CppTypeMapper.getIncludeForMemberType(bi, model, "myservice")); + assertEquals(java.util.Optional.of(""), + CppTypeMapper.getIncludeForMemberType(bd, model, "myservice")); + } + + @Test + void includesForShape_bigNumbersNestedInContainers_surfaceCrtHeaders() { + BigIntegerShape bi = BigIntegerShape.builder().id("com.example#Big").build(); + BigDecimalShape bd = BigDecimalShape.builder().id("com.example#Dec").build(); + StringShape str = StringShape.builder().id("com.example#Str").build(); + ListShape list = listOf("com.example#BigList", bi); + MapShape map = MapShape.builder().id("com.example#DecMap") + .key(MemberShape.builder().id("com.example#DecMap$key").target(str.getId()).build()) + .value(MemberShape.builder().id("com.example#DecMap$value").target(bd.getId()).build()) + .addTrait(new software.amazon.smithy.model.traits.SparseTrait()).build(); + StructureShape holder = StructureShape.builder().id("com.example#Holder") + .addMember("ids", list.getId()).addMember("amounts", map.getId()).build(); + Model model = Model.builder().addShapes(bi, bd, str, list, map, holder).build(); + List includes = CppTypeMapper.getIncludesForShape(holder, model, "myservice"); + assertTrue(includes.contains(""), includes.toString()); + assertTrue(includes.contains(""), includes.toString()); + assertTrue(includes.contains(""), includes.toString()); + } + + // --- byte / short tests --- + + @Test + void byteAndShort_mapToFixedWidthSignedTypes() { + ByteShape b = ByteShape.builder().id("com.example#B").build(); + ShortShape s = ShortShape.builder().id("com.example#S").build(); + Model model = Model.builder().addShapes(b, s).build(); + assertEquals("int8_t", CppTypeMapper.getCppType(b, model)); + assertEquals("int16_t", CppTypeMapper.getCppType(s, model)); + assertEquals("int8_t", CppTypeMapper.getCppType(b, model, true)); + assertEquals("int16_t", CppTypeMapper.getCppType(s, model, true)); + } + + @Test + void byteAndShort_arePrimitivesWithZeroDefaultAndHasBeenSet() { + ByteShape b = ByteShape.builder().id("com.example#B").build(); + ShortShape s = ShortShape.builder().id("com.example#S").build(); + for (Shape shape : new Shape[] {b, s}) { + assertTrue(CppTypeMapper.isPrimitive(shape)); + assertTrue(CppTypeMapper.needsHasBeenSetFlag(shape)); + assertEquals(Optional.of("0"), CppTypeMapper.getDefaultValue(shape)); + } + } } diff --git a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRendererTest.java b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRendererTest.java index 354199013f5..46b5325c0ae 100644 --- a/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRendererTest.java +++ b/tools/code-generation/smithy/cpp-codegen/smithy-cpp-codegen/src/test/java/com/amazonaws/util/awsclientsmithygenerator/generators/model/MemberRendererTest.java @@ -7,6 +7,8 @@ import com.amazonaws.util.awsclientsmithygenerator.generators.CppWriter; import org.junit.jupiter.api.Test; import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.BigDecimalShape; +import software.amazon.smithy.model.shapes.BigIntegerShape; import software.amazon.smithy.model.shapes.BooleanShape; import software.amazon.smithy.model.shapes.IntegerShape; import software.amazon.smithy.model.shapes.ListShape; @@ -458,4 +460,99 @@ void injectedResponseMetadata_inResult_initsFlagFalse() { assertTrue(out.contains("bool m_responseMetadataHasBeenSet = false;"), "injected ResponseMetadata flag stays false in a result context: " + out); } + + @Test + void renderPublicSection_bigIntegerMember_getterIsConstRefAndSetterForwards() { + BigIntegerShape big = BigIntegerShape.builder().id("com.example#Big").build(); + StructureShape shape = StructureShape.builder() + .id("com.example#MyShape").addMember("Amount", big.getId()).build(); + Model model = Model.builder().addShapes(big, shape).build(); + CppWriter writer = new CppWriter(); + MemberRenderer.forStructure(model, shape, "MyShape").renderPublicAccessors(writer); + String output = writer.toString(); + assertTrue(output.contains("inline const Aws::Crt::BigInteger& GetAmount() const { return m_amount; }"), output); + assertTrue(output.contains("inline bool AmountHasBeenSet() const { return m_amountHasBeenSet; }"), output); + assertTrue(output.contains("template "), output); + assertTrue(output.contains("void SetAmount(AmountT&& value)"), output); + assertTrue(output.contains("m_amount = std::forward(value);"), output); + assertFalse(output.contains("inline Aws::Crt::BigInteger GetAmount()"), "must not be a by-value getter: " + output); + } + + @Test + void renderPublicSection_bigDecimalList_addForwardsElement() { + BigDecimalShape big = BigDecimalShape.builder().id("com.example#Dec").build(); + ListShape list = ListShape.builder().id("com.example#DecList") + .member(MemberShape.builder().id("com.example#DecList$member").target(big.getId()).build()).build(); + StructureShape shape = StructureShape.builder() + .id("com.example#MyShape").addMember("Amounts", list.getId()).build(); + Model model = Model.builder().addShapes(big, list, shape).build(); + CppWriter writer = new CppWriter(); + MemberRenderer.forStructure(model, shape, "MyShape").renderPublicAccessors(writer); + String output = writer.toString(); + assertTrue(output.contains("inline const Aws::Vector& GetAmounts() const"), output); + assertTrue(output.contains("template "), output); + assertTrue(output.contains("MyShape& AddAmounts(AmountsT&& value)"), output); + assertTrue(output.contains("m_amounts.emplace_back(std::forward(value));"), output); + } + + @Test + void renderPublicSection_bigIntegerMap_addForwardsKeyAndValue() { + StringShape str = StringShape.builder().id("com.example#String").build(); + BigIntegerShape big = BigIntegerShape.builder().id("com.example#Big").build(); + MapShape map = MapShape.builder().id("com.example#BigMap") + .key(MemberShape.builder().id("com.example#BigMap$key").target(str.getId()).build()) + .value(MemberShape.builder().id("com.example#BigMap$value").target(big.getId()).build()).build(); + StructureShape shape = StructureShape.builder() + .id("com.example#MyShape").addMember("Totals", map.getId()).build(); + Model model = Model.builder().addShapes(str, big, map, shape).build(); + CppWriter writer = new CppWriter(); + MemberRenderer.forStructure(model, shape, "MyShape").renderPublicAccessors(writer); + String output = writer.toString(); + assertTrue(output.contains( + "template "), output); + assertTrue(output.contains("m_totals.emplace(std::forward(key), std::forward(value));"), + output); + } + + @Test + void renderPrivateSection_bigNumberMembers_areValueInitializedWithHasBeenSetFlags() { + BigIntegerShape bi = BigIntegerShape.builder().id("com.example#Big").build(); + BigDecimalShape bd = BigDecimalShape.builder().id("com.example#Dec").build(); + StructureShape shape = StructureShape.builder().id("com.example#MyShape") + .addMember("Amount", bi.getId()).addMember("Price", bd.getId()).build(); + Model model = Model.builder().addShapes(bi, bd, shape).build(); + CppWriter writer = new CppWriter(); + MemberRenderer.forStructure(model, shape, null).renderPrivateSection(writer); + String output = writer.toString(); + assertTrue(output.contains("Aws::Crt::BigInteger m_amount{};"), output); + assertTrue(output.contains("Aws::Crt::BigDecimal m_price{};"), output); + assertTrue(output.contains("bool m_amountHasBeenSet = false;"), output); + assertTrue(output.contains("bool m_priceHasBeenSet = false;"), output); + } + + @Test + void renderPrivateSection_bigNumberDefaultTrait_isIgnoredLikeOtherTypes() { + BigIntegerShape bi = BigIntegerShape.builder().id("com.example#Big").build(); + BigDecimalShape bd = BigDecimalShape.builder().id("com.example#Dec").build(); + IntegerShape integer = IntegerShape.builder().id("com.example#Int").build(); + StructureShape shape = StructureShape.builder().id("com.example#MyShape") + .addMember("Amount", bi.getId(), + b -> b.addTrait(new software.amazon.smithy.model.traits.DefaultTrait( + software.amazon.smithy.model.node.Node.from(0)))) + .addMember("Price", bd.getId(), + b -> b.addTrait(new software.amazon.smithy.model.traits.DefaultTrait( + software.amazon.smithy.model.node.Node.from(1.5)))) + .addMember("Count", integer.getId(), + b -> b.addTrait(new software.amazon.smithy.model.traits.DefaultTrait( + software.amazon.smithy.model.node.Node.from(5)))) + .build(); + Model model = Model.builder().addShapes(bi, bd, integer, shape).build(); + CppWriter writer = new CppWriter(); + MemberRenderer.forStructure(model, shape, null).renderPrivateSection(writer); + String output = writer.toString(); + // @default is not rendered for any type: bignums value-initialize to zero, ints keep 0. + assertTrue(output.contains("Aws::Crt::BigInteger m_amount{};"), output); + assertTrue(output.contains("Aws::Crt::BigDecimal m_price{};"), output); + assertTrue(output.contains("int m_count{0};"), output); + } } From 1d2eac9712b450157d1e35557bd36de6533a3a2f Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Thu, 5 Nov 2026 12:49:58 -0500 Subject: [PATCH 2/5] bump aws-crt-cpp to main for bignum bindings Update the crt/aws-crt-cpp submodule to 4993689, which includes awslabs/aws-crt-cpp#917, and sync the pinned dependency SHAs in prefetch_crt_dependency.sh to that commit's submodules. --- crt/aws-crt-cpp | 2 +- prefetch_crt_dependency.sh | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crt/aws-crt-cpp b/crt/aws-crt-cpp index 58bca57cbc8..4993689829a 160000 --- a/crt/aws-crt-cpp +++ b/crt/aws-crt-cpp @@ -1 +1 @@ -Subproject commit 58bca57cbc8b242468414972a6a35f92676b164d +Subproject commit 4993689829a8397d9c205863c9983900a020ecac diff --git a/prefetch_crt_dependency.sh b/prefetch_crt_dependency.sh index 96b61aad29b..9049796906c 100755 --- a/prefetch_crt_dependency.sh +++ b/prefetch_crt_dependency.sh @@ -3,21 +3,21 @@ # SPDX-License-Identifier: Apache-2.0. CRT_URI_PREFIX=https://codeload.github.com/awslabs -CRT_URI=${CRT_URI_PREFIX}/aws-crt-cpp/zip/58bca57cbc8b242468414972a6a35f92676b164d # v0.44.2 +CRT_URI=${CRT_URI_PREFIX}/aws-crt-cpp/zip/4993689829a8397d9c205863c9983900a020ecac # main (includes bignum, #917) -AWS_C_AUTH_URI=${CRT_URI_PREFIX}/aws-c-auth/zip/055e822f021d1cab11d608b94ac34de250986134 # v1.0.0 +AWS_C_AUTH_URI=${CRT_URI_PREFIX}/aws-c-auth/zip/3d29adcecf5c032131acee9f4125a636d2aaea6a # v1.0.2 AWS_C_CAL_URI=${CRT_URI_PREFIX}/aws-c-cal/zip/2441187f3bdbe0f0fe7fa5dd1736be98227ad556 # v1.0.0 -AWS_C_COMMON_URI=${CRT_URI_PREFIX}/aws-c-common/zip/2a29f7ea85397ff15e41ae086fb3662484fd305d # v1.0.3 +AWS_C_COMMON_URI=${CRT_URI_PREFIX}/aws-c-common/zip/45e9307382f196175f0dbb832a178570924bda96 # v1.0.5 AWS_C_COMPRESSION_URI=${CRT_URI_PREFIX}/aws-c-compression/zip/891312c058e69a4d55f569f79dde1ee221982532 # v1.0.0 AWS_C_EVENT_STREAM_URI=${CRT_URI_PREFIX}/aws-c-event-stream/zip/d32600bfecc6616cab0c478f96ea5a779f03e05e # v1.0.0 -AWS_C_HTTP_URI=${CRT_URI_PREFIX}/aws-c-http/zip/afe6411dd17b2368d5262724c25a3b85670b4e06 # v1.1.0 -AWS_C_IO_URI=${CRT_URI_PREFIX}/aws-c-io/zip/1831a1943e4c4a034d42f954e8f154cf6b9b492f # v1.1.0 +AWS_C_HTTP_URI=${CRT_URI_PREFIX}/aws-c-http/zip/d19eabf4c5dc78ee1e07033a5015e9d50d04ec31 # v1.1.1 +AWS_C_IO_URI=${CRT_URI_PREFIX}/aws-c-io/zip/cbc36857376bffb8f4654e6c4e443b6939e1629c # v1.1.1 AWS_C_MQTT_URI=${CRT_URI_PREFIX}/aws-c-mqtt/zip/96af751fa58d4d6704881463a4d6895b107268ed # v1.1.2 -AWS_C_S3_URI=${CRT_URI_PREFIX}/aws-c-s3/zip/eeb85f3e4ed89497a93952964bd8228d1f435e6e # v1.3.1 -AWS_C_SDKUTILS_URI=${CRT_URI_PREFIX}/aws-c-sdkutils/zip/de0fbe807a409158c3fa285bafb88120c8989609 # v1.0.0 +AWS_C_S3_URI=${CRT_URI_PREFIX}/aws-c-s3/zip/bc923d67e7aa85fa715c46bd95c0c5711148873c # v1.5.2 +AWS_C_SDKUTILS_URI=${CRT_URI_PREFIX}/aws-c-sdkutils/zip/66a79c011dbe057fcc6e9620f93a52c2c29843e8 # v1.0.1 AWS_CHECKSUMS_URI=${CRT_URI_PREFIX}/aws-checksums/zip/12cd22e81543b8547132762023076ab6afa25559 # v1.0.1 -AWS_LC_URI=${CRT_URI_PREFIX}/aws-lc/zip/fa9bc8d6f7cfb2cf849b247fb06bbbfc41ecc9d6 # v5.11.0 -S2N_URI=${CRT_URI_PREFIX}/s2n/zip/b170000016fb44fa4dfdd86ed0b75bd28241e707 # v1.7.11 +AWS_LC_URI=${CRT_URI_PREFIX}/aws-lc/zip/cd481c207e38f06aeb5766f4a1cdfce10a4c0af8 # v5.12.0 +S2N_URI=${CRT_URI_PREFIX}/s2n/zip/e7ce83a27fa9525515a621754a7183a51c80246b # v1.7.13 echo "Removing CRT" From 7e1e6f5116ca56f8b03aed9d38735994c5817eaf Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Thu, 5 Nov 2026 13:25:54 -0500 Subject: [PATCH 3/5] fix clang-format in CodecTest --- tests/aws-cpp-sdk-core-tests/smithy/client/schema/CodecTest.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CodecTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CodecTest.cpp index 82e1cf7dbfe..d4f662ca1db 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CodecTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CodecTest.cpp @@ -501,7 +501,6 @@ TEST_F(CodecTest, JsonDocumentAtReaderDepthLimitStillSerializes) { } } - TEST_F(CodecTest, CborBigNumberRoundTrip) { auto root = Schema::StructureBuilder("Root") .PutMember("big", Schema::CreateBigInteger("BI")) From de6a05d0819bfb48fe7dc20916a8bde38e0d53ba Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Fri, 6 Nov 2026 11:23:53 -0500 Subject: [PATCH 4/5] add changelog fragment for bignum support --- .changelog/feature-feat-bignum.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changelog/feature-feat-bignum.json diff --git a/.changelog/feature-feat-bignum.json b/.changelog/feature-feat-bignum.json new file mode 100644 index 00000000000..876fe62efbe --- /dev/null +++ b/.changelog/feature-feat-bignum.json @@ -0,0 +1,6 @@ +{ + "type": "feature", + "category": "aws-cpp-sdk-core", + "contributor": "lucix-aws", + "description": "Add byte, short, bigInteger, and bigDecimal support to the schema-serde ShapeSerializer and ShapeDeserializer (CBOR, JSON, XML, and Query codecs). Big numbers use Aws::Crt::BigInteger and Aws::Crt::BigDecimal, so this updates aws-crt-cpp to a main commit that includes them (aws-crt-cpp PR 917) along with its dependency pins. Adding pure virtual methods to ShapeSerializer and ShapeDeserializer is a source-breaking change for out-of-tree subclasses. HTTP bindings are not covered." +} From f53b0459ca9de3c3d84ac157ad18f6233ca92302 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Fri, 6 Nov 2026 12:32:08 -0500 Subject: [PATCH 5/5] brace gtest assertions in byte/short range tests to fix GCC dangling-else --- .../smithy/client/schema/CborShapeDeserializerTest.cpp | 8 ++++++-- .../smithy/client/schema/JsonShapeDeserializerTest.cpp | 8 ++++++-- .../smithy/client/schema/XmlShapeDeserializerTest.cpp | 8 ++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CborShapeDeserializerTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CborShapeDeserializerTest.cpp index fd1f3352f04..a7db66455df 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CborShapeDeserializerTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/CborShapeDeserializerTest.cpp @@ -776,8 +776,12 @@ TEST_F(CborShapeDeserializerTest, ByteAndShortRanges) { }); ASSERT_EQ(b.has_value(), c.byteOk); ASSERT_EQ(s.has_value(), c.shortOk); - if (c.byteOk) EXPECT_EQ(b.value(), c.byteWire); - if (c.shortOk) EXPECT_EQ(s.value(), c.shortWire); + if (c.byteOk) { + EXPECT_EQ(b.value(), c.byteWire); + } + if (c.shortOk) { + EXPECT_EQ(s.value(), c.shortWire); + } ASSERT_TRUE(after.has_value()); EXPECT_EQ(after.value(), 1); } diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeDeserializerTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeDeserializerTest.cpp index 5fedc397ebd..56b8c6c5206 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeDeserializerTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeDeserializerTest.cpp @@ -932,8 +932,12 @@ TEST_F(JsonShapeDeserializerTest, ByteAndShortRanges) { }); ASSERT_EQ(b.has_value(), c.byteOk); ASSERT_EQ(s.has_value(), c.shortOk); - if (c.byteOk) EXPECT_EQ(b.value(), c.byteValue); - if (c.shortOk) EXPECT_EQ(s.value(), c.shortValue); + if (c.byteOk) { + EXPECT_EQ(b.value(), c.byteValue); + } + if (c.shortOk) { + EXPECT_EQ(s.value(), c.shortValue); + } // An out-of-range value is consumed, so the next member still reads. ASSERT_TRUE(after.has_value()); EXPECT_EQ(after.value(), 1); diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/XmlShapeDeserializerTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/XmlShapeDeserializerTest.cpp index 384dfd056ed..2539602f541 100644 --- a/tests/aws-cpp-sdk-core-tests/smithy/client/schema/XmlShapeDeserializerTest.cpp +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/XmlShapeDeserializerTest.cpp @@ -859,8 +859,12 @@ TEST_F(XmlShapeDeserializerTest, ByteAndShortRanges) { }); ASSERT_EQ(b.has_value(), c.byteOk); ASSERT_EQ(s.has_value(), c.shortOk); - if (c.byteOk) EXPECT_EQ(b.value(), c.byteValue); - if (c.shortOk) EXPECT_EQ(s.value(), c.shortValue); + if (c.byteOk) { + EXPECT_EQ(b.value(), c.byteValue); + } + if (c.shortOk) { + EXPECT_EQ(s.value(), c.shortValue); + } ASSERT_TRUE(after.has_value()); EXPECT_EQ(after.value(), 1); }