From c2b364a77a5bd147cca0dffdb7d8555cc615b5c8 Mon Sep 17 00:00:00 2001 From: pulimsr Date: Tue, 9 Jun 2026 14:40:15 -0400 Subject: [PATCH 1/7] adding the JsonShapeSerializer --- src/aws-cpp-sdk-core/CMakeLists.txt | 10 +- .../include/smithy/client/schema/Schema.h | 2 + .../client/schema/JsonShapeSerializer.cpp | 209 ++++++++++ tests/aws-cpp-sdk-core-tests/CMakeLists.txt | 1 + .../client/schema/JsonShapeSerializerTest.cpp | 364 ++++++++++++++++++ 5 files changed, 585 insertions(+), 1 deletion(-) create mode 100644 src/aws-cpp-sdk-core/source/smithy/client/schema/JsonShapeSerializer.cpp create mode 100644 tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeSerializerTest.cpp diff --git a/src/aws-cpp-sdk-core/CMakeLists.txt b/src/aws-cpp-sdk-core/CMakeLists.txt index 14ae520433e3..007d30752407 100644 --- a/src/aws-cpp-sdk-core/CMakeLists.txt +++ b/src/aws-cpp-sdk-core/CMakeLists.txt @@ -543,7 +543,15 @@ check_cxx_source_compiles(" return 0; }" AWS_HAS_ALIGNED_ALLOC) -add_library(${PROJECT_NAME} ${AWS_NATIVE_SDK_SRC}) +add_library(${PROJECT_NAME} ${AWS_NATIVE_SDK_SRC} + include/smithy/client/schema/ShapeSerializer.h + include/smithy/client/schema/CborShapeSerializer.h + include/smithy/client/schema/Schema.h + include/smithy/client/schema/JsonShapeSerializer.h + include/smithy/client/schema/QueryShapeSerializer.h + include/smithy/client/schema/XmlShapeSerializer.h + source/smithy/client/schema/JsonShapeSerializer.cpp +) add_library(AWS::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) target_compile_definitions(${PROJECT_NAME} PUBLIC "AWS_SDK_VERSION_MAJOR=${AWSSDK_VERSION_MAJOR}") diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/Schema.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/Schema.h index a8a8cacb3a61..4cbe8e366ae7 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/Schema.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/Schema.h @@ -35,6 +35,8 @@ enum class ShapeType : uint8_t { class Schema { public: Schema() = default; + Schema(const char* memberName, ShapeType type) + : m_type(type), m_memberName(memberName) {} ShapeType GetType() const { return m_type; } const char* GetId() const { return m_id; } 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 new file mode 100644 index 000000000000..f1368900dcdc --- /dev/null +++ b/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonShapeSerializer.cpp @@ -0,0 +1,209 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include +#include +#include + +using namespace smithy::schema; +using namespace Aws::Utils; +using namespace Aws::Utils::Json; + +struct JsonShapeSerializer::Impl { + JsonValue m_root; + + struct StackEntry { + JsonValue object; + const char* fieldName = nullptr; + bool isList = false; + bool isMap = false; + Aws::Vector listItems; + }; + + Aws::Vector m_stack; + Aws::String m_currentMapKey; + + JsonValue& CurrentObject() { + if (m_stack.empty()) return m_root; + return m_stack.back().object; + } + + const char* CurrentKey(const Schema& schema) { + if (!m_stack.empty() && m_stack.back().isMap) return m_currentMapKey.c_str(); + return schema.GetMemberName(); + } + + void BeginStructure(const Schema&) {} + void EndStructure() {} + + void WriteBoolean(const Schema& schema, bool value) { + if (!m_stack.empty() && m_stack.back().isList) { + JsonValue v; + v.AsBool(value); + m_stack.back().listItems.push_back(std::move(v)); + } else { + CurrentObject().WithBool(CurrentKey(schema), value); + } + } + + void WriteInteger(const Schema& schema, int value) { + if (!m_stack.empty() && m_stack.back().isList) { + JsonValue v; + v.AsInteger(value); + m_stack.back().listItems.push_back(std::move(v)); + } else { + CurrentObject().WithInteger(CurrentKey(schema), value); + } + } + + void WriteLong(const Schema& schema, int64_t value) { + if (!m_stack.empty() && m_stack.back().isList) { + JsonValue v; + v.AsInt64(value); + m_stack.back().listItems.push_back(std::move(v)); + } else { + CurrentObject().WithInt64(CurrentKey(schema), value); + } + } + + void WriteDouble(const Schema& schema, double value) { + if (!m_stack.empty() && m_stack.back().isList) { + JsonValue v; + v.AsDouble(value); + m_stack.back().listItems.push_back(std::move(v)); + } else { + CurrentObject().WithDouble(CurrentKey(schema), value); + } + } + + void WriteString(const Schema& schema, const Aws::String& value) { + if (!m_stack.empty() && m_stack.back().isList) { + JsonValue v; + v.AsString(value); + m_stack.back().listItems.push_back(std::move(v)); + } else { + CurrentObject().WithString(CurrentKey(schema), value); + } + } + + void WriteTimestamp(const Schema& schema, const DateTime& value) { + if (!m_stack.empty() && m_stack.back().isList) { + JsonValue v; + v.AsDouble(value.SecondsWithMSPrecision()); + m_stack.back().listItems.push_back(std::move(v)); + } else { + CurrentObject().WithDouble(CurrentKey(schema), value.SecondsWithMSPrecision()); + } + } + + void WriteBlob(const Schema& schema, const ByteBuffer& value) { + Aws::String encoded = HashingUtils::Base64Encode(value); + if (!m_stack.empty() && m_stack.back().isList) { + JsonValue v; + v.AsString(encoded); + m_stack.back().listItems.push_back(std::move(v)); + } else { + CurrentObject().WithString(CurrentKey(schema), encoded); + } + } + + void WriteEnum(const Schema& schema, int value) { WriteInteger(schema, value); } + + void WriteNull(const Schema& schema) { + if (!m_stack.empty() && m_stack.back().isList) { + JsonValue v; + v.AsNull(); + m_stack.back().listItems.push_back(std::move(v)); + } else { + CurrentObject().WithNull(CurrentKey(schema)); + } + } + + void BeginList(const Schema& schema, size_t) { + StackEntry entry; + entry.fieldName = CurrentKey(schema); + entry.isList = true; + m_stack.push_back(std::move(entry)); + } + + void EndList() { + auto entry = std::move(m_stack.back()); + m_stack.pop_back(); + + Array arr(entry.listItems.size()); + for (size_t i = 0; i < entry.listItems.size(); ++i) { + arr[i] = std::move(entry.listItems[i]); + } + + if (!m_stack.empty() && m_stack.back().isList) { + JsonValue listVal; + listVal.WithArray("", std::move(arr)); + m_stack.back().listItems.push_back(std::move(listVal)); + } else { + CurrentObject().WithArray(entry.fieldName, std::move(arr)); + } + } + + void BeginMap(const Schema& schema, size_t) { + StackEntry entry; + entry.fieldName = CurrentKey(schema); + entry.isMap = true; + m_stack.push_back(std::move(entry)); + } + + void WriteMapKey(const Aws::String& key) { m_currentMapKey = key; } + + void EndMap() { + auto entry = std::move(m_stack.back()); + m_stack.pop_back(); + + if (!m_stack.empty() && m_stack.back().isList) { + m_stack.back().listItems.push_back(std::move(entry.object)); + } else { + CurrentObject().WithObject(entry.fieldName, std::move(entry.object)); + } + } + + void BeginNestedStructure(const Schema& schema) { + StackEntry entry; + entry.fieldName = CurrentKey(schema); + m_stack.push_back(std::move(entry)); + } + + void EndNestedStructure() { + auto entry = std::move(m_stack.back()); + m_stack.pop_back(); + + if (!m_stack.empty() && m_stack.back().isList) { + m_stack.back().listItems.push_back(std::move(entry.object)); + } else { + CurrentObject().WithObject(entry.fieldName, std::move(entry.object)); + } + } + + Aws::String GetPayload() const { return m_root.View().WriteCompact(); } +}; + +JsonShapeSerializer::JsonShapeSerializer() : m_impl(new Impl) {} +JsonShapeSerializer::~JsonShapeSerializer() = default; + +void JsonShapeSerializer::BeginStructure(const Schema& schema) { m_impl->BeginStructure(schema); } +void JsonShapeSerializer::EndStructure() { m_impl->EndStructure(); } +void JsonShapeSerializer::WriteBoolean(const Schema& schema, bool value) { m_impl->WriteBoolean(schema, value); } +void JsonShapeSerializer::WriteInteger(const Schema& schema, int value) { m_impl->WriteInteger(schema, value); } +void JsonShapeSerializer::WriteLong(const Schema& schema, int64_t value) { m_impl->WriteLong(schema, value); } +void JsonShapeSerializer::WriteDouble(const Schema& schema, double value) { m_impl->WriteDouble(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); } +void JsonShapeSerializer::WriteEnum(const Schema& schema, int value) { m_impl->WriteEnum(schema, value); } +void JsonShapeSerializer::WriteNull(const Schema& schema) { m_impl->WriteNull(schema); } +void JsonShapeSerializer::BeginList(const Schema& schema, size_t count) { m_impl->BeginList(schema, count); } +void JsonShapeSerializer::EndList() { m_impl->EndList(); } +void JsonShapeSerializer::BeginMap(const Schema& schema, size_t count) { m_impl->BeginMap(schema, count); } +void JsonShapeSerializer::WriteMapKey(const Aws::String& key) { m_impl->WriteMapKey(key); } +void JsonShapeSerializer::EndMap() { m_impl->EndMap(); } +void JsonShapeSerializer::BeginNestedStructure(const Schema& schema) { m_impl->BeginNestedStructure(schema); } +void JsonShapeSerializer::EndNestedStructure() { m_impl->EndNestedStructure(); } +Aws::String JsonShapeSerializer::GetPayload() const { return m_impl->GetPayload(); } diff --git a/tests/aws-cpp-sdk-core-tests/CMakeLists.txt b/tests/aws-cpp-sdk-core-tests/CMakeLists.txt index 36cdad1290c5..4ce72d351177 100644 --- a/tests/aws-cpp-sdk-core-tests/CMakeLists.txt +++ b/tests/aws-cpp-sdk-core-tests/CMakeLists.txt @@ -27,6 +27,7 @@ file(GLOB UTILS_COMPONENT_REGISTRY_SRC "${CMAKE_CURRENT_SOURCE_DIR}/utils/compon file(GLOB MONITORING_SRC "${CMAKE_CURRENT_SOURCE_DIR}/monitoring/*.cpp") file(GLOB SMITHY_TRACING_SRC "${CMAKE_CURRENT_SOURCE_DIR}/smithy/tracing/*.cpp") file(GLOB SMITHY_CLIENT_SRC "${CMAKE_CURRENT_SOURCE_DIR}/smithy/client/*.cpp") +file(GLOB SMITHY_CLIENT_SRC "${CMAKE_CURRENT_SOURCE_DIR}/smithy/client/schema/*.cpp") file(GLOB SMITHY_CLIENT_SERIALIZER_SRC "${CMAKE_CURRENT_SOURCE_DIR}/smithy/client/serializer/*.cpp") file(GLOB SMITHY_CLIENT_FEATURE_SRC "${CMAKE_CURRENT_SOURCE_DIR}/smithy/client/feature/*.cpp") file(GLOB ENDPOINT_SRC "${CMAKE_CURRENT_SOURCE_DIR}/endpoint/*.cpp") 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 new file mode 100644 index 000000000000..bd9ba79ac926 --- /dev/null +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/schema/JsonShapeSerializerTest.cpp @@ -0,0 +1,364 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include +#include +#include + +using namespace smithy::schema; + +class JsonShapeSerializerTest : public Aws::Testing::AwsCppSdkGTestSuite {}; + +// --- Scalars --- + +TEST_F(JsonShapeSerializerTest, EmptyStructure) { + JsonShapeSerializer s; + Schema root; + s.BeginStructure(root); + s.EndStructure(); + EXPECT_EQ(s.GetPayload(), "{}"); +} + +TEST_F(JsonShapeSerializerTest, BooleanTrue) { + JsonShapeSerializer s; + Schema root; + Schema member("enabled", ShapeType::Boolean); + s.BeginStructure(root); + s.WriteBoolean(member, true); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"enabled\":true"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, BooleanFalse) { + JsonShapeSerializer s; + Schema root; + Schema member("enabled", ShapeType::Boolean); + s.BeginStructure(root); + s.WriteBoolean(member, false); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"enabled\":false"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, Integer) { + JsonShapeSerializer s; + Schema root; + Schema member("count", ShapeType::Integer); + s.BeginStructure(root); + s.WriteInteger(member, 42); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"count\":42"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, Long) { + JsonShapeSerializer s; + Schema root; + Schema member("bigNum", ShapeType::Long); + s.BeginStructure(root); + s.WriteLong(member, 9876543210LL); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"bigNum\":9876543210"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, Double) { + JsonShapeSerializer s; + Schema root; + Schema member("ratio", ShapeType::Double); + s.BeginStructure(root); + s.WriteDouble(member, 3.14); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"ratio\":3.14"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, String) { + JsonShapeSerializer s; + Schema root; + Schema member("name", ShapeType::String); + s.BeginStructure(root); + s.WriteString(member, "hello"); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"name\":\"hello\""), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, EmptyString) { + JsonShapeSerializer s; + Schema root; + Schema member("name", ShapeType::String); + s.BeginStructure(root); + s.WriteString(member, ""); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"name\":\"\""), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, Timestamp) { + JsonShapeSerializer s; + Schema root; + Schema member("created", ShapeType::Timestamp); + s.BeginStructure(root); + Aws::Utils::DateTime dt(1234567890.0); + s.WriteTimestamp(member, dt); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"created\":"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, Blob) { + JsonShapeSerializer s; + Schema root; + Schema member("data", ShapeType::Blob); + s.BeginStructure(root); + unsigned char raw[] = {0x66, 0x6f, 0x6f}; + Aws::Utils::ByteBuffer buf(raw, 3); + s.WriteBlob(member, buf); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"data\":\"Zm9v\""), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, NullValue) { + JsonShapeSerializer s; + Schema root; + Schema member("item", ShapeType::String); + s.BeginStructure(root); + s.WriteNull(member); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"item\":null"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, MultipleScalars) { + JsonShapeSerializer s; + Schema root; + Schema m1("a", ShapeType::Boolean); + Schema m2("b", ShapeType::Integer); + Schema m3("c", ShapeType::String); + s.BeginStructure(root); + s.WriteBoolean(m1, true); + s.WriteInteger(m2, 7); + s.WriteString(m3, "x"); + s.EndStructure(); + auto payload = s.GetPayload(); + EXPECT_NE(payload.find("\"a\":true"), Aws::String::npos); + EXPECT_NE(payload.find("\"b\":7"), Aws::String::npos); + EXPECT_NE(payload.find("\"c\":\"x\""), Aws::String::npos); +} + +// --- Nested structures --- + +TEST_F(JsonShapeSerializerTest, NestedStructure) { + JsonShapeSerializer s; + Schema root; + Schema nested("metadata", ShapeType::Structure); + Schema inner("key", ShapeType::String); + s.BeginStructure(root); + s.BeginNestedStructure(nested); + s.WriteString(inner, "val"); + s.EndNestedStructure(); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"metadata\":{\"key\":\"val\"}"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, DeeplyNestedStructure) { + JsonShapeSerializer s; + Schema root; + Schema level1("l1", ShapeType::Structure); + Schema level2("l2", ShapeType::Structure); + Schema leaf("val", ShapeType::Integer); + s.BeginStructure(root); + s.BeginNestedStructure(level1); + s.BeginNestedStructure(level2); + s.WriteInteger(leaf, 99); + s.EndNestedStructure(); + s.EndNestedStructure(); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"l1\":{\"l2\":{\"val\":99}}"), Aws::String::npos); +} + +// --- Lists --- + +TEST_F(JsonShapeSerializerTest, ListOfStrings) { + JsonShapeSerializer s; + Schema root; + Schema listMember("tags", ShapeType::List); + Schema elem("member", ShapeType::String); + s.BeginStructure(root); + s.BeginList(listMember, 3); + s.WriteString(elem, "a"); + s.WriteString(elem, "b"); + s.WriteString(elem, "c"); + s.EndList(); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"tags\":[\"a\",\"b\",\"c\"]"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, ListOfIntegers) { + JsonShapeSerializer s; + Schema root; + Schema listMember("nums", ShapeType::List); + Schema elem("member", ShapeType::Integer); + s.BeginStructure(root); + s.BeginList(listMember, 3); + s.WriteInteger(elem, 1); + s.WriteInteger(elem, 2); + s.WriteInteger(elem, 3); + s.EndList(); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"nums\":[1,2,3]"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, EmptyList) { + JsonShapeSerializer s; + Schema root; + Schema listMember("items", ShapeType::List); + s.BeginStructure(root); + s.BeginList(listMember, 0); + s.EndList(); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"items\":[]"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, ListOfStructures) { + JsonShapeSerializer s; + Schema root; + Schema listMember("items", ShapeType::List); + Schema structElem("member", ShapeType::Structure); + Schema field("id", ShapeType::Integer); + s.BeginStructure(root); + s.BeginList(listMember, 2); + s.BeginNestedStructure(structElem); + s.WriteInteger(field, 1); + s.EndNestedStructure(); + s.BeginNestedStructure(structElem); + s.WriteInteger(field, 2); + s.EndNestedStructure(); + s.EndList(); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"items\":[{\"id\":1},{\"id\":2}]"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, SparseList) { + JsonShapeSerializer s; + Schema root; + Schema listMember("items", ShapeType::List); + Schema elem("member", ShapeType::String); + s.BeginStructure(root); + s.BeginList(listMember, 3); + s.WriteString(elem, "a"); + s.WriteNull(elem); + s.WriteString(elem, "b"); + s.EndList(); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"items\":[\"a\",null,\"b\"]"), Aws::String::npos); +} + +// --- Maps --- + +TEST_F(JsonShapeSerializerTest, MapOfStrings) { + JsonShapeSerializer s; + Schema root; + Schema mapMember("headers", ShapeType::Map); + Schema valSchema("value", ShapeType::String); + s.BeginStructure(root); + s.BeginMap(mapMember, 2); + s.WriteMapKey("x-foo"); + s.WriteString(valSchema, "bar"); + s.WriteMapKey("x-baz"); + s.WriteString(valSchema, "qux"); + s.EndMap(); + s.EndStructure(); + auto payload = s.GetPayload(); + EXPECT_NE(payload.find("\"x-foo\":\"bar\""), Aws::String::npos); + EXPECT_NE(payload.find("\"x-baz\":\"qux\""), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, EmptyMap) { + JsonShapeSerializer s; + Schema root; + Schema mapMember("tags", ShapeType::Map); + s.BeginStructure(root); + s.BeginMap(mapMember, 0); + s.EndMap(); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"tags\":{}"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, MapOfStructures) { + JsonShapeSerializer s; + Schema root; + Schema mapMember("nodes", ShapeType::Map); + Schema valSchema("value", ShapeType::Structure); + Schema field("val", ShapeType::Integer); + s.BeginStructure(root); + s.BeginMap(mapMember, 1); + s.WriteMapKey("a"); + s.BeginNestedStructure(valSchema); + s.WriteInteger(field, 1); + s.EndNestedStructure(); + s.EndMap(); + s.EndStructure(); + EXPECT_NE(s.GetPayload().find("\"nodes\":{\"a\":{\"val\":1}}"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, SparseMap) { + JsonShapeSerializer s; + Schema root; + Schema mapMember("data", ShapeType::Map); + Schema valSchema("value", ShapeType::String); + s.BeginStructure(root); + s.BeginMap(mapMember, 2); + s.WriteMapKey("present"); + s.WriteString(valSchema, "yes"); + s.WriteMapKey("absent"); + s.WriteNull(valSchema); + s.EndMap(); + s.EndStructure(); + auto payload = s.GetPayload(); + EXPECT_NE(payload.find("\"present\":\"yes\""), Aws::String::npos); + EXPECT_NE(payload.find("\"absent\":null"), Aws::String::npos); +} + +// --- Combinations --- + +TEST_F(JsonShapeSerializerTest, StructureWithListAndMap) { + JsonShapeSerializer s; + Schema root; + Schema strMember("name", ShapeType::String); + Schema listMember("tags", ShapeType::List); + Schema listElem("member", ShapeType::String); + Schema mapMember("meta", ShapeType::Map); + Schema mapVal("value", ShapeType::String); + + s.BeginStructure(root); + s.WriteString(strMember, "test"); + s.BeginList(listMember, 2); + s.WriteString(listElem, "t1"); + s.WriteString(listElem, "t2"); + s.EndList(); + s.BeginMap(mapMember, 1); + s.WriteMapKey("k"); + s.WriteString(mapVal, "v"); + s.EndMap(); + s.EndStructure(); + + auto payload = s.GetPayload(); + EXPECT_NE(payload.find("\"name\":\"test\""), Aws::String::npos); + EXPECT_NE(payload.find("\"tags\":[\"t1\",\"t2\"]"), Aws::String::npos); + EXPECT_NE(payload.find("\"meta\":{\"k\":\"v\"}"), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, MapContainingList) { + JsonShapeSerializer s; + Schema root; + Schema mapMember("data", ShapeType::Map); + Schema listSchema("value", ShapeType::List); + Schema elem("member", ShapeType::Integer); + + s.BeginStructure(root); + s.BeginMap(mapMember, 1); + s.WriteMapKey("nums"); + s.BeginList(listSchema, 2); + s.WriteInteger(elem, 1); + s.WriteInteger(elem, 2); + s.EndList(); + s.EndMap(); + s.EndStructure(); + + EXPECT_NE(s.GetPayload().find("\"data\":{\"nums\":[1,2]}"), Aws::String::npos); +} From d95ac61ae6fafc62c7e075c8d1d1b497cb53ed7c Mon Sep 17 00:00:00 2001 From: pulimsr Date: Wed, 10 Jun 2026 16:45:51 -0400 Subject: [PATCH 2/7] using a string buffer --- .../client/schema/JsonShapeSerializer.cpp | 211 ++++++++---------- 1 file changed, 90 insertions(+), 121 deletions(-) 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 f1368900dcdc..ca8dba329322 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 @@ -3,189 +3,158 @@ * SPDX-License-Identifier: Apache-2.0. */ #include -#include #include using namespace smithy::schema; using namespace Aws::Utils; -using namespace Aws::Utils::Json; + +static const int MAX_DEPTH = 64; struct JsonShapeSerializer::Impl { - JsonValue m_root; + Aws::String m_buf; + int m_depth = 0; + bool m_needsComma[MAX_DEPTH] = {}; + bool m_isMap[MAX_DEPTH] = {}; + bool m_isList[MAX_DEPTH] = {}; + Aws::String m_currentMapKey; - struct StackEntry { - JsonValue object; - const char* fieldName = nullptr; - bool isList = false; - bool isMap = false; - Aws::Vector listItems; - }; + void WriteCommaIfNeeded() { + if (m_needsComma[m_depth]) { + m_buf += ','; + } else { + m_needsComma[m_depth] = true; + } + } - Aws::Vector m_stack; - Aws::String m_currentMapKey; + void WriteKey(const char* key) { + m_buf += '"'; + m_buf += key; + m_buf += "\":"; + } - JsonValue& CurrentObject() { - if (m_stack.empty()) return m_root; - return m_stack.back().object; + void WriteFieldName(const Schema& schema) { + WriteCommaIfNeeded(); + if (m_isList[m_depth]) { + return; + } + if (m_depth > 0 && m_isMap[m_depth]) { + WriteKey(m_currentMapKey.c_str()); + } else { + WriteKey(schema.GetMemberName()); + } } - const char* CurrentKey(const Schema& schema) { - if (!m_stack.empty() && m_stack.back().isMap) return m_currentMapKey.c_str(); - return schema.GetMemberName(); + void BeginStructure(const Schema&) { + m_buf += '{'; + m_depth++; + m_needsComma[m_depth] = false; + m_isMap[m_depth] = false; + m_isList[m_depth] = false; } - void BeginStructure(const Schema&) {} - void EndStructure() {} + void EndStructure() { + m_depth--; + m_buf += '}'; + } void WriteBoolean(const Schema& schema, bool value) { - if (!m_stack.empty() && m_stack.back().isList) { - JsonValue v; - v.AsBool(value); - m_stack.back().listItems.push_back(std::move(v)); - } else { - CurrentObject().WithBool(CurrentKey(schema), value); - } + WriteFieldName(schema); + m_buf += value ? "true" : "false"; } void WriteInteger(const Schema& schema, int value) { - if (!m_stack.empty() && m_stack.back().isList) { - JsonValue v; - v.AsInteger(value); - m_stack.back().listItems.push_back(std::move(v)); - } else { - CurrentObject().WithInteger(CurrentKey(schema), value); - } + WriteFieldName(schema); + m_buf += std::to_string(value); } void WriteLong(const Schema& schema, int64_t value) { - if (!m_stack.empty() && m_stack.back().isList) { - JsonValue v; - v.AsInt64(value); - m_stack.back().listItems.push_back(std::move(v)); - } else { - CurrentObject().WithInt64(CurrentKey(schema), value); - } + WriteFieldName(schema); + m_buf += std::to_string(value); } void WriteDouble(const Schema& schema, double value) { - if (!m_stack.empty() && m_stack.back().isList) { - JsonValue v; - v.AsDouble(value); - m_stack.back().listItems.push_back(std::move(v)); - } else { - CurrentObject().WithDouble(CurrentKey(schema), value); - } + WriteFieldName(schema); + char tmp[32]; + snprintf(tmp, sizeof(tmp), "%g", value); + m_buf += tmp; } void WriteString(const Schema& schema, const Aws::String& value) { - if (!m_stack.empty() && m_stack.back().isList) { - JsonValue v; - v.AsString(value); - m_stack.back().listItems.push_back(std::move(v)); - } else { - CurrentObject().WithString(CurrentKey(schema), value); - } + WriteFieldName(schema); + m_buf += '"'; + m_buf += value; + m_buf += '"'; } void WriteTimestamp(const Schema& schema, const DateTime& value) { - if (!m_stack.empty() && m_stack.back().isList) { - JsonValue v; - v.AsDouble(value.SecondsWithMSPrecision()); - m_stack.back().listItems.push_back(std::move(v)); - } else { - CurrentObject().WithDouble(CurrentKey(schema), value.SecondsWithMSPrecision()); - } + WriteFieldName(schema); + char tmp[32]; + snprintf(tmp, sizeof(tmp), "%g", value.SecondsWithMSPrecision()); + m_buf += tmp; } void WriteBlob(const Schema& schema, const ByteBuffer& value) { - Aws::String encoded = HashingUtils::Base64Encode(value); - if (!m_stack.empty() && m_stack.back().isList) { - JsonValue v; - v.AsString(encoded); - m_stack.back().listItems.push_back(std::move(v)); - } else { - CurrentObject().WithString(CurrentKey(schema), encoded); - } + WriteFieldName(schema); + m_buf += '"'; + m_buf += HashingUtils::Base64Encode(value); + m_buf += '"'; } void WriteEnum(const Schema& schema, int value) { WriteInteger(schema, value); } void WriteNull(const Schema& schema) { - if (!m_stack.empty() && m_stack.back().isList) { - JsonValue v; - v.AsNull(); - m_stack.back().listItems.push_back(std::move(v)); - } else { - CurrentObject().WithNull(CurrentKey(schema)); - } + WriteFieldName(schema); + m_buf += "null"; } void BeginList(const Schema& schema, size_t) { - StackEntry entry; - entry.fieldName = CurrentKey(schema); - entry.isList = true; - m_stack.push_back(std::move(entry)); + WriteFieldName(schema); + m_buf += '['; + m_depth++; + m_needsComma[m_depth] = false; + m_isMap[m_depth] = false; + m_isList[m_depth] = true; } void EndList() { - auto entry = std::move(m_stack.back()); - m_stack.pop_back(); - - Array arr(entry.listItems.size()); - for (size_t i = 0; i < entry.listItems.size(); ++i) { - arr[i] = std::move(entry.listItems[i]); - } - - if (!m_stack.empty() && m_stack.back().isList) { - JsonValue listVal; - listVal.WithArray("", std::move(arr)); - m_stack.back().listItems.push_back(std::move(listVal)); - } else { - CurrentObject().WithArray(entry.fieldName, std::move(arr)); - } + m_depth--; + m_buf += ']'; } void BeginMap(const Schema& schema, size_t) { - StackEntry entry; - entry.fieldName = CurrentKey(schema); - entry.isMap = true; - m_stack.push_back(std::move(entry)); + WriteFieldName(schema); + m_buf += '{'; + m_depth++; + m_needsComma[m_depth] = false; + m_isMap[m_depth] = true; + m_isList[m_depth] = false; } void WriteMapKey(const Aws::String& key) { m_currentMapKey = key; } void EndMap() { - auto entry = std::move(m_stack.back()); - m_stack.pop_back(); - - if (!m_stack.empty() && m_stack.back().isList) { - m_stack.back().listItems.push_back(std::move(entry.object)); - } else { - CurrentObject().WithObject(entry.fieldName, std::move(entry.object)); - } + m_depth--; + m_buf += '}'; } void BeginNestedStructure(const Schema& schema) { - StackEntry entry; - entry.fieldName = CurrentKey(schema); - m_stack.push_back(std::move(entry)); + WriteFieldName(schema); + m_buf += '{'; + m_depth++; + m_needsComma[m_depth] = false; + m_isMap[m_depth] = false; + m_isList[m_depth] = false; } void EndNestedStructure() { - auto entry = std::move(m_stack.back()); - m_stack.pop_back(); - - if (!m_stack.empty() && m_stack.back().isList) { - m_stack.back().listItems.push_back(std::move(entry.object)); - } else { - CurrentObject().WithObject(entry.fieldName, std::move(entry.object)); - } + m_depth--; + m_buf += '}'; } - Aws::String GetPayload() const { return m_root.View().WriteCompact(); } + Aws::String GetPayload() const { return m_buf; } }; -JsonShapeSerializer::JsonShapeSerializer() : m_impl(new Impl) {} +JsonShapeSerializer::JsonShapeSerializer() : m_impl(new Impl) { m_impl->m_buf.reserve(256); } JsonShapeSerializer::~JsonShapeSerializer() = default; void JsonShapeSerializer::BeginStructure(const Schema& schema) { m_impl->BeginStructure(schema); } From bdb15ac612174872dc26775e9c3c5c6934b7bf00 Mon Sep 17 00:00:00 2001 From: pulimsr Date: Mon, 15 Jun 2026 11:23:56 -0400 Subject: [PATCH 3/7] removing unnecessary heap allocations --- .../smithy/client/schema/JsonShapeSerializer.h | 2 +- .../client/schema/JsonShapeSerializer.cpp | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) 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 e58c81bc80f0..d2a474ce56f0 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 @@ -35,7 +35,7 @@ class JsonShapeSerializer final : public ShapeSerializer { void BeginNestedStructure(const Schema& schema) override; void EndNestedStructure() override; - Aws::String GetPayload() const; + const Aws::String& GetPayload() const; private: struct Impl; 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 ca8dba329322..6710ef51a571 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 @@ -16,7 +16,7 @@ struct JsonShapeSerializer::Impl { bool m_needsComma[MAX_DEPTH] = {}; bool m_isMap[MAX_DEPTH] = {}; bool m_isList[MAX_DEPTH] = {}; - Aws::String m_currentMapKey; + const char* m_currentMapKey = nullptr; void WriteCommaIfNeeded() { if (m_needsComma[m_depth]) { @@ -38,7 +38,7 @@ struct JsonShapeSerializer::Impl { return; } if (m_depth > 0 && m_isMap[m_depth]) { - WriteKey(m_currentMapKey.c_str()); + WriteKey(m_currentMapKey); } else { WriteKey(schema.GetMemberName()); } @@ -64,12 +64,16 @@ struct JsonShapeSerializer::Impl { void WriteInteger(const Schema& schema, int value) { WriteFieldName(schema); - m_buf += std::to_string(value); + char tmp[16]; + snprintf(tmp, sizeof(tmp), "%d", value); + m_buf += tmp; } void WriteLong(const Schema& schema, int64_t value) { WriteFieldName(schema); - m_buf += std::to_string(value); + char tmp[24]; + snprintf(tmp, sizeof(tmp), "%lld", static_cast(value)); + m_buf += tmp; } void WriteDouble(const Schema& schema, double value) { @@ -130,7 +134,7 @@ struct JsonShapeSerializer::Impl { m_isList[m_depth] = false; } - void WriteMapKey(const Aws::String& key) { m_currentMapKey = key; } + void WriteMapKey(const Aws::String& key) { m_currentMapKey = key.c_str(); } void EndMap() { m_depth--; @@ -151,7 +155,7 @@ struct JsonShapeSerializer::Impl { m_buf += '}'; } - Aws::String GetPayload() const { return m_buf; } + const Aws::String& GetPayload() const { return m_buf; } }; JsonShapeSerializer::JsonShapeSerializer() : m_impl(new Impl) { m_impl->m_buf.reserve(256); } @@ -175,4 +179,4 @@ void JsonShapeSerializer::WriteMapKey(const Aws::String& key) { m_impl->WriteMap void JsonShapeSerializer::EndMap() { m_impl->EndMap(); } void JsonShapeSerializer::BeginNestedStructure(const Schema& schema) { m_impl->BeginNestedStructure(schema); } void JsonShapeSerializer::EndNestedStructure() { m_impl->EndNestedStructure(); } -Aws::String JsonShapeSerializer::GetPayload() const { return m_impl->GetPayload(); } +const Aws::String& JsonShapeSerializer::GetPayload() const { return m_impl->GetPayload(); } From ca248df69129f35f416491003be81e5860851d0a Mon Sep 17 00:00:00 2001 From: pulimsr Date: Tue, 16 Jun 2026 12:07:44 -0400 Subject: [PATCH 4/7] Updating to Aws::UniquePtr and removing temp variables --- .../client/schema/CborShapeSerializer.h | 5 +-- .../client/schema/JsonShapeSerializer.h | 6 +-- .../client/schema/QueryShapeSerializer.h | 4 +- .../smithy/client/schema/XmlShapeSerializer.h | 4 +- .../client/schema/JsonShapeSerializer.cpp | 41 ++++++++++++------- 5 files changed, 35 insertions(+), 25 deletions(-) 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 8809866c09bf..d3a32c329001 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 @@ -2,8 +2,7 @@ #include #include - -#include +#include namespace smithy { namespace schema { @@ -40,7 +39,7 @@ class AWS_CORE_API CborShapeSerialize final : public ShapeSerializer { private: struct Impl; - std::unique_ptr m_impl; + Aws::UniquePtr m_impl; }; } // namespace schema 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 d2a474ce56f0..60b5a7a554bb 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 @@ -2,7 +2,7 @@ #include -#include +#include namespace smithy { namespace schema { @@ -35,11 +35,11 @@ class JsonShapeSerializer final : public ShapeSerializer { void BeginNestedStructure(const Schema& schema) override; void EndNestedStructure() override; - const Aws::String& GetPayload() const; + Aws::String GetPayload(); private: struct Impl; - std::unique_ptr m_impl; + Aws::UniquePtr m_impl; }; } // namespace schema 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 040968a0b12d..5725dd00d765 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 @@ -2,7 +2,7 @@ #include -#include +#include namespace smithy { namespace schema { @@ -39,7 +39,7 @@ class QueryShapeSerializer final : public ShapeSerializer { private: struct Impl; - std::unique_ptr m_impl; + Aws::UniquePtr m_impl; }; } // namespace schema 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 88ce7a5de39a..ea841d6c23ee 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 @@ -2,7 +2,7 @@ #include -#include +#include namespace smithy { namespace schema { @@ -39,7 +39,7 @@ class XmlShapeSerializer final : public ShapeSerializer { private: struct Impl; - std::unique_ptr m_impl; + Aws::UniquePtr m_impl; }; } // namespace 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 6710ef51a571..f0c4c9887bce 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 @@ -5,6 +5,8 @@ #include #include +#include + using namespace smithy::schema; using namespace Aws::Utils; @@ -17,6 +19,7 @@ struct JsonShapeSerializer::Impl { bool m_isMap[MAX_DEPTH] = {}; bool m_isList[MAX_DEPTH] = {}; const char* m_currentMapKey = nullptr; + bool m_finalized = false; void WriteCommaIfNeeded() { if (m_needsComma[m_depth]) { @@ -64,23 +67,26 @@ struct JsonShapeSerializer::Impl { void WriteInteger(const Schema& schema, int value) { WriteFieldName(schema); - char tmp[16]; - snprintf(tmp, sizeof(tmp), "%d", value); - m_buf += tmp; + auto start = m_buf.size(); + m_buf.resize(start + 11); + int written = snprintf(&m_buf[start],12,"%d", value); + m_buf.resize(start + written); } void WriteLong(const Schema& schema, int64_t value) { WriteFieldName(schema); - char tmp[24]; - snprintf(tmp, sizeof(tmp), "%lld", static_cast(value)); - m_buf += tmp; + auto start = m_buf.size(); + m_buf.resize(start+20); + int writter = snprintf(&m_buf[start],21,"%lld", static_cast(value)); + m_buf.resize(start + writter); } void WriteDouble(const Schema& schema, double value) { WriteFieldName(schema); - char tmp[32]; - snprintf(tmp, sizeof(tmp), "%g", value); - m_buf += tmp; + auto start = m_buf.size(); + m_buf.resize(start+32); + int writter = snprintf(&m_buf[start],32,"%f", value); + m_buf.resize(start + writter); } void WriteString(const Schema& schema, const Aws::String& value) { @@ -92,9 +98,10 @@ struct JsonShapeSerializer::Impl { void WriteTimestamp(const Schema& schema, const DateTime& value) { WriteFieldName(schema); - char tmp[32]; - snprintf(tmp, sizeof(tmp), "%g", value.SecondsWithMSPrecision()); - m_buf += tmp; + auto start = m_buf.size(); + m_buf.resize(start+32); + int written = snprintf(&m_buf[start], 32, "%g", value.SecondsWithMSPrecision()); + m_buf.resize(start + written); } void WriteBlob(const Schema& schema, const ByteBuffer& value) { @@ -155,10 +162,14 @@ struct JsonShapeSerializer::Impl { m_buf += '}'; } - const Aws::String& GetPayload() const { return m_buf; } + Aws::String GetPayload() { + assert(!m_finalized); + m_finalized = true; + return std::move(m_buf); + } }; -JsonShapeSerializer::JsonShapeSerializer() : m_impl(new Impl) { m_impl->m_buf.reserve(256); } +JsonShapeSerializer::JsonShapeSerializer() : m_impl(Aws::MakeUnique("JsonShapeSerializer")) { m_impl->m_buf.reserve(8192); } JsonShapeSerializer::~JsonShapeSerializer() = default; void JsonShapeSerializer::BeginStructure(const Schema& schema) { m_impl->BeginStructure(schema); } @@ -179,4 +190,4 @@ void JsonShapeSerializer::WriteMapKey(const Aws::String& key) { m_impl->WriteMap void JsonShapeSerializer::EndMap() { m_impl->EndMap(); } void JsonShapeSerializer::BeginNestedStructure(const Schema& schema) { m_impl->BeginNestedStructure(schema); } void JsonShapeSerializer::EndNestedStructure() { m_impl->EndNestedStructure(); } -const Aws::String& JsonShapeSerializer::GetPayload() const { return m_impl->GetPayload(); } +Aws::String JsonShapeSerializer::GetPayload() { return m_impl->GetPayload(); } From 774dcb627fc738d258cac9b8a611b23286bd0fb8 Mon Sep 17 00:00:00 2001 From: pulimsr Date: Thu, 25 Jun 2026 10:44:48 -0400 Subject: [PATCH 5/7] adding escaping, Outcome error handling, and depth enforcement --- src/aws-cpp-sdk-core/CMakeLists.txt | 2 + .../client/schema/CborShapeSerializer.h | 10 +- .../client/schema/JsonShapeSerializer.h | 15 +- .../smithy/client/schema/JsonWriteUtils.h | 16 ++ .../client/schema/QueryShapeSerializer.h | 10 +- .../include/smithy/client/schema/Schema.h | 8 +- .../smithy/client/schema/ShapeSerializer.h | 8 +- .../smithy/client/schema/XmlShapeSerializer.h | 10 +- .../client/schema/JsonShapeSerializer.cpp | 163 +++++++++++------- .../smithy/client/schema/JsonWriteUtils.cpp | 80 +++++++++ .../client/schema/JsonShapeSerializerTest.cpp | 127 +++++++++++--- 11 files changed, 330 insertions(+), 119 deletions(-) create mode 100644 src/aws-cpp-sdk-core/include/smithy/client/schema/JsonWriteUtils.h create mode 100644 src/aws-cpp-sdk-core/source/smithy/client/schema/JsonWriteUtils.cpp diff --git a/src/aws-cpp-sdk-core/CMakeLists.txt b/src/aws-cpp-sdk-core/CMakeLists.txt index 007d30752407..d4f5d074a849 100644 --- a/src/aws-cpp-sdk-core/CMakeLists.txt +++ b/src/aws-cpp-sdk-core/CMakeLists.txt @@ -551,6 +551,8 @@ add_library(${PROJECT_NAME} ${AWS_NATIVE_SDK_SRC} include/smithy/client/schema/QueryShapeSerializer.h include/smithy/client/schema/XmlShapeSerializer.h source/smithy/client/schema/JsonShapeSerializer.cpp + include/smithy/client/schema/JsonWriteUtils.h + source/smithy/client/schema/JsonWriteUtils.cpp ) add_library(AWS::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 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 d3a32c329001..59097b64fd23 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 @@ -12,7 +12,7 @@ class AWS_CORE_API CborShapeSerialize final : public ShapeSerializer { CborShapeSerializer(); ~CborShapeSerializer(); - void BeginStructure(const Schema& schema) override; + bool BeginStructure(const Schema& schema) override; void EndStructure() override; void WriteBoolean(const Schema& schema, bool value) override; @@ -25,20 +25,20 @@ class AWS_CORE_API CborShapeSerialize final : public ShapeSerializer { void WriteEnum(const Schema& schema, int value) override; void WriteNull(const Schema& schema) override; - void BeginList(const Schema& schema, size_t count) override; + bool BeginList(const Schema& schema, size_t count) override; void EndList() override; - void BeginMap(const Schema& schema, size_t count) override; + bool BeginMap(const Schema& schema, size_t count) override; void WriteMapKey(const Aws::String& key) override; void EndMap() override; - void BeginNestedStructure(const Schema& schema) override; + bool BeginNestedStructure(const Schema& schema) override; void EndNestedStructure() override; Aws::String GetPayload() const; private: - struct Impl; + class Impl; Aws::UniquePtr m_impl; }; 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 60b5a7a554bb..81f553719be7 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 @@ -4,15 +4,18 @@ #include +#include "aws/core/client/AWSError.h" + namespace smithy { namespace schema { class JsonShapeSerializer final : public ShapeSerializer { public: + using SerializerOutcome = Aws::Utils::Outcome>; JsonShapeSerializer(); ~JsonShapeSerializer(); - void BeginStructure(const Schema& schema) override; + bool BeginStructure(const Schema& schema) override; void EndStructure() override; void WriteBoolean(const Schema& schema, bool value) override; @@ -25,20 +28,20 @@ class JsonShapeSerializer final : public ShapeSerializer { void WriteEnum(const Schema& schema, int value) override; void WriteNull(const Schema& schema) override; - void BeginList(const Schema& schema, size_t count) override; + bool BeginList(const Schema& schema, size_t count) override; void EndList() override; - void BeginMap(const Schema& schema, size_t count) override; + bool BeginMap(const Schema& schema, size_t count) override; void WriteMapKey(const Aws::String& key) override; void EndMap() override; - void BeginNestedStructure(const Schema& schema) override; + bool BeginNestedStructure(const Schema& schema) override; void EndNestedStructure() override; - Aws::String GetPayload(); + SerializerOutcome GetPayload(); private: - struct Impl; + class Impl; Aws::UniquePtr m_impl; }; diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonWriteUtils.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonWriteUtils.h new file mode 100644 index 000000000000..14a37cce86d6 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonWriteUtils.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace smithy { + namespace schema { + class JsonWriteUtils { + public: + static void WriteQuotedString(Aws::String& buf, const Aws::String& value); + private: + static const bool NEEDS_ESCAPE[128]; + static const char ESCAPE_TABLE[128]; + static const char HEX[16]; + }; + } +} \ No newline at end of file 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 5725dd00d765..c3f5e521727f 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 @@ -12,7 +12,7 @@ class QueryShapeSerializer final : public ShapeSerializer { QueryShapeSerializer(const Aws::String& action, const Aws::String& version); ~QueryShapeSerializer(); - void BeginStructure(const Schema& schema) override; + bool BeginStructure(const Schema& schema) override; void EndStructure() override; void WriteBoolean(const Schema& schema, bool value) override; @@ -25,20 +25,20 @@ class QueryShapeSerializer final : public ShapeSerializer { void WriteEnum(const Schema& schema, int value) override; void WriteNull(const Schema& schema) override; - void BeginList(const Schema& schema, size_t count) override; + bool BeginList(const Schema& schema, size_t count) override; void EndList() override; - void BeginMap(const Schema& schema, size_t count) override; + bool BeginMap(const Schema& schema, size_t count) override; void WriteMapKey(const Aws::String& key) override; void EndMap() override; - void BeginNestedStructure(const Schema& schema) override; + bool BeginNestedStructure(const Schema& schema) override; void EndNestedStructure() override; Aws::String GetPayload() const; private: - struct Impl; + class Impl; Aws::UniquePtr m_impl; }; diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/Schema.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/Schema.h index 4cbe8e366ae7..03172f23ae9f 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/Schema.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/Schema.h @@ -35,14 +35,14 @@ enum class ShapeType : uint8_t { class Schema { public: Schema() = default; - Schema(const char* memberName, ShapeType type) + Schema(const Aws::String& memberName, ShapeType type) : m_type(type), m_memberName(memberName) {} ShapeType GetType() const { return m_type; } const char* GetId() const { return m_id; } - const char* GetMemberName() const { return m_memberName; } + const Aws::String& GetMemberName() const { return m_memberName; } int GetMemberIndex() const { return m_memberIndex; } - bool IsMember() const { return m_memberName != nullptr; } + bool IsMember() const { return !m_memberName.empty(); } const Schema* GetMember(const char* name) const; const Schema* GetMember(int index) const; @@ -52,7 +52,7 @@ class Schema { private: const char* m_id = nullptr; ShapeType m_type = ShapeType::Structure; - const char* m_memberName = nullptr; + Aws::String m_memberName; int m_memberIndex = 0; const Schema* m_members = nullptr; uint16_t m_memberCount = 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 41279e7f5a9b..abea197156b6 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 @@ -14,7 +14,7 @@ class ShapeSerializer { public: virtual ~ShapeSerializer() = default; - virtual void BeginStructure(const Schema& schema) = 0; + virtual bool BeginStructure(const Schema& schema) = 0; virtual void EndStructure() = 0; virtual void WriteBoolean(const Schema& schema, bool value) = 0; @@ -27,14 +27,14 @@ class ShapeSerializer { virtual void WriteEnum(const Schema& schema, int value) = 0; virtual void WriteNull(const Schema& schema) = 0; - virtual void BeginList(const Schema& schema, size_t count) = 0; + virtual bool BeginList(const Schema& schema, size_t count) = 0; virtual void EndList() = 0; - virtual void BeginMap(const Schema& schema, size_t count) = 0; + virtual bool BeginMap(const Schema& schema, size_t count) = 0; virtual void WriteMapKey(const Aws::String& key) = 0; virtual void EndMap() = 0; - virtual void BeginNestedStructure(const Schema& schema) = 0; + virtual bool BeginNestedStructure(const Schema& schema) = 0; virtual void EndNestedStructure() = 0; }; 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 ea841d6c23ee..0362fa69bea7 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 @@ -12,7 +12,7 @@ class XmlShapeSerializer final : public ShapeSerializer { XmlShapeSerializer(); ~XmlShapeSerializer(); - void BeginStructure(const Schema& schema) override; + bool BeginStructure(const Schema& schema) override; void EndStructure() override; void WriteBoolean(const Schema& schema, bool value) override; @@ -25,20 +25,20 @@ class XmlShapeSerializer final : public ShapeSerializer { void WriteEnum(const Schema& schema, int value) override; void WriteNull(const Schema& schema) override; - void BeginList(const Schema& schema, size_t count) override; + bool BeginList(const Schema& schema, size_t count) override; void EndList() override; - void BeginMap(const Schema& schema, size_t count) override; + bool BeginMap(const Schema& schema, size_t count) override; void WriteMapKey(const Aws::String& key) override; void EndMap() override; - void BeginNestedStructure(const Schema& schema) override; + bool BeginNestedStructure(const Schema& schema) override; void EndNestedStructure() override; Aws::String GetPayload() const; private: - struct Impl; + class Impl; Aws::UniquePtr m_impl; }; 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 f0c4c9887bce..c0af9762e3dc 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 @@ -3,56 +3,40 @@ * SPDX-License-Identifier: Apache-2.0. */ #include +#include #include +#include -#include +#include + +#include "aws/core/client/AWSClient.h" +#include "aws/core/utils/Outcome.h" +#include "aws/core/utils/memory/stl/AWSArray.h" using namespace smithy::schema; using namespace Aws::Utils; +using SerializerOutcome = Aws::Utils::Outcome>; -static const int MAX_DEPTH = 64; - -struct JsonShapeSerializer::Impl { - Aws::String m_buf; - int m_depth = 0; - bool m_needsComma[MAX_DEPTH] = {}; - bool m_isMap[MAX_DEPTH] = {}; - bool m_isList[MAX_DEPTH] = {}; - const char* m_currentMapKey = nullptr; - bool m_finalized = false; +static constexpr int MAX_DEPTH = 1000; - void WriteCommaIfNeeded() { - if (m_needsComma[m_depth]) { - m_buf += ','; - } else { - m_needsComma[m_depth] = true; - } - } +class JsonShapeSerializer::Impl { - void WriteKey(const char* key) { - m_buf += '"'; - m_buf += key; - m_buf += "\":"; - } +public: + Impl() {m_buf.reserve(8192);} - void WriteFieldName(const Schema& schema) { - WriteCommaIfNeeded(); - if (m_isList[m_depth]) { - return; - } - if (m_depth > 0 && m_isMap[m_depth]) { - WriteKey(m_currentMapKey); - } else { - WriteKey(schema.GetMemberName()); + bool BeginStructure(const Schema&) { + if (m_depth + 1 >= MAX_DEPTH) { + m_errorMessage = "Maximum nesting depth exceeded"; + return false; } - } - void BeginStructure(const Schema&) { m_buf += '{'; m_depth++; m_needsComma[m_depth] = false; m_isMap[m_depth] = false; m_isList[m_depth] = false; + + return true; } void EndStructure() { @@ -67,41 +51,27 @@ struct JsonShapeSerializer::Impl { void WriteInteger(const Schema& schema, int value) { WriteFieldName(schema); - auto start = m_buf.size(); - m_buf.resize(start + 11); - int written = snprintf(&m_buf[start],12,"%d", value); - m_buf.resize(start + written); + m_buf += StringUtils::to_string(value); } void WriteLong(const Schema& schema, int64_t value) { WriteFieldName(schema); - auto start = m_buf.size(); - m_buf.resize(start+20); - int writter = snprintf(&m_buf[start],21,"%lld", static_cast(value)); - m_buf.resize(start + writter); + m_buf += StringUtils::to_string(value); } void WriteDouble(const Schema& schema, double value) { WriteFieldName(schema); - auto start = m_buf.size(); - m_buf.resize(start+32); - int writter = snprintf(&m_buf[start],32,"%f", value); - m_buf.resize(start + writter); + m_buf += StringUtils::to_string(value); } void WriteString(const Schema& schema, const Aws::String& value) { WriteFieldName(schema); - m_buf += '"'; - m_buf += value; - m_buf += '"'; + JsonWriteUtils::WriteQuotedString(m_buf, value); } void WriteTimestamp(const Schema& schema, const DateTime& value) { WriteFieldName(schema); - auto start = m_buf.size(); - m_buf.resize(start+32); - int written = snprintf(&m_buf[start], 32, "%g", value.SecondsWithMSPrecision()); - m_buf.resize(start + written); + m_buf += StringUtils::to_string(value.SecondsWithMSPrecision()); } void WriteBlob(const Schema& schema, const ByteBuffer& value) { @@ -118,13 +88,20 @@ struct JsonShapeSerializer::Impl { m_buf += "null"; } - void BeginList(const Schema& schema, size_t) { + bool BeginList(const Schema& schema, size_t) { + if (m_depth + 1 >= MAX_DEPTH) { + m_errorMessage = "Maximum nesting depth exceeded"; + return false; + } + WriteFieldName(schema); m_buf += '['; m_depth++; m_needsComma[m_depth] = false; m_isMap[m_depth] = false; m_isList[m_depth] = true; + + return true; } void EndList() { @@ -132,29 +109,43 @@ struct JsonShapeSerializer::Impl { m_buf += ']'; } - void BeginMap(const Schema& schema, size_t) { + bool BeginMap(const Schema& schema, size_t) { + if (m_depth + 1 >= MAX_DEPTH) { + m_errorMessage = "Maximum nesting depth exceeded"; + return false; + } + WriteFieldName(schema); m_buf += '{'; m_depth++; m_needsComma[m_depth] = false; m_isMap[m_depth] = true; m_isList[m_depth] = false; + + return true; } - void WriteMapKey(const Aws::String& key) { m_currentMapKey = key.c_str(); } + void WriteMapKey(const Aws::String& key) { m_currentMapKey = key; } void EndMap() { m_depth--; m_buf += '}'; } - void BeginNestedStructure(const Schema& schema) { + bool BeginNestedStructure(const Schema& schema) { + if (m_depth + 1 >= MAX_DEPTH) { + m_errorMessage = "Maximum nesting depth exceeded"; + return false; + } + WriteFieldName(schema); m_buf += '{'; m_depth++; m_needsComma[m_depth] = false; m_isMap[m_depth] = false; m_isList[m_depth] = false; + + return true; } void EndNestedStructure() { @@ -162,17 +153,59 @@ struct JsonShapeSerializer::Impl { m_buf += '}'; } - Aws::String GetPayload() { - assert(!m_finalized); + SerializerOutcome GetPayload() { + if(m_finalized || !m_errorMessage.empty()) { + return Aws::Client::AWSError( + Aws::Client::CoreErrors::INTERNAL_FAILURE, + "SerializationException", + !m_errorMessage.empty()?m_errorMessage:"Serializer has already been finalized", + false); + } m_finalized = true; return std::move(m_buf); } + +private: + Aws::String m_buf; + int m_depth = 0; + Aws::Array m_needsComma{}; + Aws::Array m_isMap{}; + Aws::Array m_isList{}; + Aws::String m_currentMapKey; + bool m_finalized = false; + Aws::String m_errorMessage; + + void WriteCommaIfNeeded() { + if (m_needsComma[m_depth]) { + m_buf += ','; + } else { + m_needsComma[m_depth] = true; + } + } + + void WriteKey(const Aws::String& key) { + JsonWriteUtils::WriteQuotedString(m_buf, key); + m_buf += ':'; + } + + void WriteFieldName(const Schema& schema) { + WriteCommaIfNeeded(); + if (m_isList[m_depth]) { + return; + } + if (m_depth > 0 && m_isMap[m_depth]) { + WriteKey(m_currentMapKey); + } else { + WriteKey(schema.GetMemberName()); + } + } + }; -JsonShapeSerializer::JsonShapeSerializer() : m_impl(Aws::MakeUnique("JsonShapeSerializer")) { m_impl->m_buf.reserve(8192); } +JsonShapeSerializer::JsonShapeSerializer() : m_impl(Aws::MakeUnique("JsonShapeSerializer")) {} JsonShapeSerializer::~JsonShapeSerializer() = default; -void JsonShapeSerializer::BeginStructure(const Schema& schema) { m_impl->BeginStructure(schema); } +bool JsonShapeSerializer::BeginStructure(const Schema& schema) { return m_impl->BeginStructure(schema); } void JsonShapeSerializer::EndStructure() { m_impl->EndStructure(); } void JsonShapeSerializer::WriteBoolean(const Schema& schema, bool value) { m_impl->WriteBoolean(schema, value); } void JsonShapeSerializer::WriteInteger(const Schema& schema, int value) { m_impl->WriteInteger(schema, value); } @@ -183,11 +216,11 @@ void JsonShapeSerializer::WriteTimestamp(const Schema& schema, const DateTime& v void JsonShapeSerializer::WriteBlob(const Schema& schema, const ByteBuffer& value) { m_impl->WriteBlob(schema, value); } void JsonShapeSerializer::WriteEnum(const Schema& schema, int value) { m_impl->WriteEnum(schema, value); } void JsonShapeSerializer::WriteNull(const Schema& schema) { m_impl->WriteNull(schema); } -void JsonShapeSerializer::BeginList(const Schema& schema, size_t count) { m_impl->BeginList(schema, count); } +bool JsonShapeSerializer::BeginList(const Schema& schema, size_t count) { return m_impl->BeginList(schema, count); } void JsonShapeSerializer::EndList() { m_impl->EndList(); } -void JsonShapeSerializer::BeginMap(const Schema& schema, size_t count) { m_impl->BeginMap(schema, count); } +bool JsonShapeSerializer::BeginMap(const Schema& schema, size_t count) { return m_impl->BeginMap(schema, count); } void JsonShapeSerializer::WriteMapKey(const Aws::String& key) { m_impl->WriteMapKey(key); } void JsonShapeSerializer::EndMap() { m_impl->EndMap(); } -void JsonShapeSerializer::BeginNestedStructure(const Schema& schema) { m_impl->BeginNestedStructure(schema); } +bool JsonShapeSerializer::BeginNestedStructure(const Schema& schema) { return m_impl->BeginNestedStructure(schema); } void JsonShapeSerializer::EndNestedStructure() { m_impl->EndNestedStructure(); } -Aws::String JsonShapeSerializer::GetPayload() { return m_impl->GetPayload(); } +JsonShapeSerializer::SerializerOutcome JsonShapeSerializer::GetPayload() { return m_impl->GetPayload(); } diff --git a/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonWriteUtils.cpp b/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonWriteUtils.cpp new file mode 100644 index 000000000000..069b4960fde0 --- /dev/null +++ b/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonWriteUtils.cpp @@ -0,0 +1,80 @@ +#include + +using namespace smithy::schema; + +const char JsonWriteUtils::HEX[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + +const bool JsonWriteUtils::NEEDS_ESCAPE[128] = { + // 0x00-0x1F: control chars + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + // 0x20-0x2F: space ! " # $ % & ' ( ) * + , - . / + 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, + // 0x30-0x3F + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + // 0x40-0x4F + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + // 0x50-0x5F: P-Z [ \ ] ^ _ + 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, + // 0x60-0x6F + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + // 0x70-0x7F + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; + +const char JsonWriteUtils::ESCAPE_TABLE[128] = { + // 0x00-0x0F + 0,0,0,0,0,0,0,0,'b','t','n',0,'f','r',0,0, + // 0x10-0x1F + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + // 0x20-0x2F + 0,0,'"',0,0,0,0,0,0,0,0,0,0,0,0,0, + // 0x30-0x3F + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + // 0x40-0x4F + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + // 0x50-0x5F + 0,0,0,0,0,0,0,0,0,0,0,0,'\\',0,0,0, + // 0x60-0x6F + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + // 0x70-0x7F + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; + +void JsonWriteUtils::WriteQuotedString(Aws::String& buf, const Aws::String& value) { + buf += '"'; + const char* data = value.data(); + const size_t len = value.size(); + + size_t i = 0; + while (i < len) { + // Fast scan: find next char that needs escaping + size_t start = i; + while (i < len) { + unsigned char c = static_cast(data[i]); + if (c < 0x80 && NEEDS_ESCAPE[c]) break; + i++; + } + // Bulk append safe run + if (i > start) { + buf.append(data + start, i - start); + } + // Escape one char + if (i < len) { + unsigned char c = static_cast(data[i]); + if (ESCAPE_TABLE[c] != 0) { + buf += '\\'; + buf += ESCAPE_TABLE[c]; + } else { + buf += '\\'; + buf += 'u'; + buf += '0'; + buf += '0'; + buf += HEX[(c >> 4) & 0xF]; + buf += HEX[c & 0xF]; + } + i++; + } + } + buf += '"'; +} 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 bd9ba79ac926..b794ff40afa6 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 @@ -17,7 +17,9 @@ TEST_F(JsonShapeSerializerTest, EmptyStructure) { Schema root; s.BeginStructure(root); s.EndStructure(); - EXPECT_EQ(s.GetPayload(), "{}"); + auto outcome = s.GetPayload(); + ASSERT_TRUE(outcome.IsSuccess()); + EXPECT_EQ(outcome.GetResult(), "{}"); } TEST_F(JsonShapeSerializerTest, BooleanTrue) { @@ -27,7 +29,7 @@ TEST_F(JsonShapeSerializerTest, BooleanTrue) { s.BeginStructure(root); s.WriteBoolean(member, true); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"enabled\":true"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"enabled\":true"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, BooleanFalse) { @@ -37,7 +39,7 @@ TEST_F(JsonShapeSerializerTest, BooleanFalse) { s.BeginStructure(root); s.WriteBoolean(member, false); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"enabled\":false"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"enabled\":false"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, Integer) { @@ -47,7 +49,7 @@ TEST_F(JsonShapeSerializerTest, Integer) { s.BeginStructure(root); s.WriteInteger(member, 42); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"count\":42"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"count\":42"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, Long) { @@ -57,7 +59,7 @@ TEST_F(JsonShapeSerializerTest, Long) { s.BeginStructure(root); s.WriteLong(member, 9876543210LL); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"bigNum\":9876543210"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"bigNum\":9876543210"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, Double) { @@ -67,7 +69,7 @@ TEST_F(JsonShapeSerializerTest, Double) { s.BeginStructure(root); s.WriteDouble(member, 3.14); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"ratio\":3.14"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"ratio\":3.14"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, String) { @@ -77,7 +79,7 @@ TEST_F(JsonShapeSerializerTest, String) { s.BeginStructure(root); s.WriteString(member, "hello"); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"name\":\"hello\""), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"name\":\"hello\""), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, EmptyString) { @@ -87,7 +89,7 @@ TEST_F(JsonShapeSerializerTest, EmptyString) { s.BeginStructure(root); s.WriteString(member, ""); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"name\":\"\""), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"name\":\"\""), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, Timestamp) { @@ -98,7 +100,7 @@ TEST_F(JsonShapeSerializerTest, Timestamp) { Aws::Utils::DateTime dt(1234567890.0); s.WriteTimestamp(member, dt); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"created\":"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"created\":"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, Blob) { @@ -110,7 +112,7 @@ TEST_F(JsonShapeSerializerTest, Blob) { Aws::Utils::ByteBuffer buf(raw, 3); s.WriteBlob(member, buf); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"data\":\"Zm9v\""), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"data\":\"Zm9v\""), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, NullValue) { @@ -120,7 +122,7 @@ TEST_F(JsonShapeSerializerTest, NullValue) { s.BeginStructure(root); s.WriteNull(member); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"item\":null"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"item\":null"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, MultipleScalars) { @@ -134,7 +136,7 @@ TEST_F(JsonShapeSerializerTest, MultipleScalars) { s.WriteInteger(m2, 7); s.WriteString(m3, "x"); s.EndStructure(); - auto payload = s.GetPayload(); + auto payload = s.GetPayload().GetResult(); EXPECT_NE(payload.find("\"a\":true"), Aws::String::npos); EXPECT_NE(payload.find("\"b\":7"), Aws::String::npos); EXPECT_NE(payload.find("\"c\":\"x\""), Aws::String::npos); @@ -152,7 +154,7 @@ TEST_F(JsonShapeSerializerTest, NestedStructure) { s.WriteString(inner, "val"); s.EndNestedStructure(); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"metadata\":{\"key\":\"val\"}"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"metadata\":{\"key\":\"val\"}"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, DeeplyNestedStructure) { @@ -168,7 +170,7 @@ TEST_F(JsonShapeSerializerTest, DeeplyNestedStructure) { s.EndNestedStructure(); s.EndNestedStructure(); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"l1\":{\"l2\":{\"val\":99}}"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"l1\":{\"l2\":{\"val\":99}}"), Aws::String::npos); } // --- Lists --- @@ -185,7 +187,7 @@ TEST_F(JsonShapeSerializerTest, ListOfStrings) { s.WriteString(elem, "c"); s.EndList(); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"tags\":[\"a\",\"b\",\"c\"]"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"tags\":[\"a\",\"b\",\"c\"]"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, ListOfIntegers) { @@ -200,7 +202,7 @@ TEST_F(JsonShapeSerializerTest, ListOfIntegers) { s.WriteInteger(elem, 3); s.EndList(); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"nums\":[1,2,3]"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"nums\":[1,2,3]"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, EmptyList) { @@ -211,7 +213,7 @@ TEST_F(JsonShapeSerializerTest, EmptyList) { s.BeginList(listMember, 0); s.EndList(); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"items\":[]"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"items\":[]"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, ListOfStructures) { @@ -230,7 +232,7 @@ TEST_F(JsonShapeSerializerTest, ListOfStructures) { s.EndNestedStructure(); s.EndList(); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"items\":[{\"id\":1},{\"id\":2}]"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"items\":[{\"id\":1},{\"id\":2}]"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, SparseList) { @@ -245,7 +247,7 @@ TEST_F(JsonShapeSerializerTest, SparseList) { s.WriteString(elem, "b"); s.EndList(); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"items\":[\"a\",null,\"b\"]"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"items\":[\"a\",null,\"b\"]"), Aws::String::npos); } // --- Maps --- @@ -263,7 +265,7 @@ TEST_F(JsonShapeSerializerTest, MapOfStrings) { s.WriteString(valSchema, "qux"); s.EndMap(); s.EndStructure(); - auto payload = s.GetPayload(); + auto payload = s.GetPayload().GetResult(); EXPECT_NE(payload.find("\"x-foo\":\"bar\""), Aws::String::npos); EXPECT_NE(payload.find("\"x-baz\":\"qux\""), Aws::String::npos); } @@ -276,7 +278,7 @@ TEST_F(JsonShapeSerializerTest, EmptyMap) { s.BeginMap(mapMember, 0); s.EndMap(); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"tags\":{}"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"tags\":{}"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, MapOfStructures) { @@ -293,7 +295,7 @@ TEST_F(JsonShapeSerializerTest, MapOfStructures) { s.EndNestedStructure(); s.EndMap(); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"nodes\":{\"a\":{\"val\":1}}"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"nodes\":{\"a\":{\"val\":1}}"), Aws::String::npos); } TEST_F(JsonShapeSerializerTest, SparseMap) { @@ -309,7 +311,7 @@ TEST_F(JsonShapeSerializerTest, SparseMap) { s.WriteNull(valSchema); s.EndMap(); s.EndStructure(); - auto payload = s.GetPayload(); + auto payload = s.GetPayload().GetResult(); EXPECT_NE(payload.find("\"present\":\"yes\""), Aws::String::npos); EXPECT_NE(payload.find("\"absent\":null"), Aws::String::npos); } @@ -337,7 +339,7 @@ TEST_F(JsonShapeSerializerTest, StructureWithListAndMap) { s.EndMap(); s.EndStructure(); - auto payload = s.GetPayload(); + auto payload = s.GetPayload().GetResult(); EXPECT_NE(payload.find("\"name\":\"test\""), Aws::String::npos); EXPECT_NE(payload.find("\"tags\":[\"t1\",\"t2\"]"), Aws::String::npos); EXPECT_NE(payload.find("\"meta\":{\"k\":\"v\"}"), Aws::String::npos); @@ -360,5 +362,80 @@ TEST_F(JsonShapeSerializerTest, MapContainingList) { s.EndMap(); s.EndStructure(); - EXPECT_NE(s.GetPayload().find("\"data\":{\"nums\":[1,2]}"), Aws::String::npos); + EXPECT_NE(s.GetPayload().GetResult().find("\"data\":{\"nums\":[1,2]}"), Aws::String::npos); +} + +// --- JSON Escaping --- + +TEST_F(JsonShapeSerializerTest, EscapesQuotesInString) { + JsonShapeSerializer s; + Schema root; + Schema member("msg", ShapeType::String); + s.BeginStructure(root); + s.WriteString(member, "say \"hello\""); + s.EndStructure(); + EXPECT_NE(s.GetPayload().GetResult().find("\"msg\":\"say \\\"hello\\\"\""), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, EscapesBackslash) { + JsonShapeSerializer s; + Schema root; + Schema member("path", ShapeType::String); + s.BeginStructure(root); + s.WriteString(member, "C:\\Users\\test"); + s.EndStructure(); + EXPECT_NE(s.GetPayload().GetResult().find("\"path\":\"C:\\\\Users\\\\test\""), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, EscapesControlCharacters) { + JsonShapeSerializer s; + Schema root; + Schema member("text", ShapeType::String); + s.BeginStructure(root); + s.WriteString(member, "line1\nline2\ttab"); + s.EndStructure(); + EXPECT_NE(s.GetPayload().GetResult().find("\"text\":\"line1\\nline2\\ttab\""), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, EscapesNullByte) { + JsonShapeSerializer s; + Schema root; + Schema member("data", ShapeType::String); + s.BeginStructure(root); + Aws::String val("ab"); + val += '\0'; + val += "cd"; + s.WriteString(member, val); + s.EndStructure(); + EXPECT_NE(s.GetPayload().GetResult().find("\"data\":\"ab\\u0000cd\""), Aws::String::npos); +} + +TEST_F(JsonShapeSerializerTest, EscapesInMapKey) { + JsonShapeSerializer s; + Schema root; + Schema mapMember("m", ShapeType::Map); + Schema valSchema("value", ShapeType::String); + s.BeginStructure(root); + s.BeginMap(mapMember, 1); + s.WriteMapKey("key\"with\"quotes"); + s.WriteString(valSchema, "v"); + s.EndMap(); + s.EndStructure(); + EXPECT_NE(s.GetPayload().GetResult().find("\"key\\\"with\\\"quotes\":\"v\""), Aws::String::npos); +} + +// --- Depth limit --- + +TEST_F(JsonShapeSerializerTest, MaxDepthEnforcement) { + JsonShapeSerializer s; + Schema root; + Schema nested("n", ShapeType::Structure); + s.BeginStructure(root); + // Nest 1000+ times to exceed MAX_DEPTH + for (int i = 0; i < 1000; i++) { + s.BeginNestedStructure(nested); + } + auto outcome = s.GetPayload(); + ASSERT_FALSE(outcome.IsSuccess()); + EXPECT_NE(outcome.GetError().GetMessage().find("depth"), Aws::String::npos); } From 3b01877ef3f992be31ab675762a0db969af21f42 Mon Sep 17 00:00:00 2001 From: pulimsr Date: Fri, 26 Jun 2026 11:13:23 -0400 Subject: [PATCH 6/7] updating clang-format, glob CMake, switch-based escaping, Schema return by value --- src/aws-cpp-sdk-core/CMakeLists.txt | 17 +-- .../client/schema/CborShapeSerializer.h | 2 +- .../client/schema/JsonShapeSerializer.h | 8 +- .../smithy/client/schema/JsonWriteUtils.h | 20 +-- .../client/schema/QueryShapeSerializer.h | 3 +- .../include/smithy/client/schema/Schema.h | 5 +- .../smithy/client/schema/XmlShapeSerializer.h | 3 +- .../client/schema/JsonShapeSerializer.cpp | 26 ++-- .../smithy/client/schema/JsonWriteUtils.cpp | 135 +++++++++--------- 9 files changed, 97 insertions(+), 122 deletions(-) diff --git a/src/aws-cpp-sdk-core/CMakeLists.txt b/src/aws-cpp-sdk-core/CMakeLists.txt index d4f5d074a849..abc6668d0cb0 100644 --- a/src/aws-cpp-sdk-core/CMakeLists.txt +++ b/src/aws-cpp-sdk-core/CMakeLists.txt @@ -97,6 +97,7 @@ file(GLOB SMITHY_IDENTITY_SIGNER_HEADERS "include/smithy/identity/signer/*.h") file(GLOB SMITHY_IDENTITY_SIGNER_BUILTIN_HEADERS "include/smithy/identity/signer/built-in/*.h") file(GLOB SMITHY_INTERCEPTOR_HEADERS "include/smithy/interceptor/*.h") file(GLOB SMITHY_INTERCEPTOR_IMPL_HEADERS "include/smithy/interceptor/impl/*.h") +file(GLOB SMITHY_CLIENT_SCHEMA_HEADERS "include/smithy/client/schema/*.h") file(GLOB AWS_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp") file(GLOB AWS_TINYXML2_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/source/external/tinyxml2/*.cpp") @@ -136,6 +137,7 @@ file(GLOB SMITHY_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/source/smithy/*.cpp") file(GLOB SMITHY_CLIENT_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/source/smithy/client/*.cpp") file(GLOB SMITHY_IDENTITY_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/source/smithy/identity/*.cpp") file(GLOB SMITHY_TRACING_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/source/smithy/tracing/*.cpp") +file(GLOB SMITHY_CLIENT_SCHEMA_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/source/smithy/client/schema/*.cpp") include(CheckCSourceCompiles) include(CheckCXXSourceCompiles) @@ -316,6 +318,7 @@ file(GLOB AWS_NATIVE_SDK_COMMON_HEADERS ${OPTEL_HEADERS} ${SMITHY_INTERCEPTOR_HEADERS} ${SMITHY_INTERCEPTOR_IMPL_HEADERS} + ${SMITHY_CLIENT_SCHEMA_HEADERS} ) # misc platform-specific, not related to features (encryption/http clients) @@ -378,6 +381,7 @@ file(GLOB AWS_NATIVE_SDK_NON_UNITY_SRC ${UTILS_CHECKSUM_SOURCE} ${UTILS_CRYPTO_SOURCE} ${OPTEL_SOURCE} + ${SMITHY_CLIENT_SCHEMA_SOURCE} ) file(GLOB AWS_NATIVE_SDK_UNITY_SRC @@ -462,6 +466,7 @@ if(MSVC) source_group("Header Files\\smithy\\identity\\signer\\built-in" FILES ${SMITHY_IDENTITY_SIGNER_BUILTIN_HEADERS}) source_group("Header Files\\smithy\\interceptor" FILES ${SMITHY_INTERCEPTOR_HEADERS}) source_group("Header Files\\smithy\\interceptor" FILES ${SMITHY_INTERCEPTOR_IMPL_HEADERS}) + source_group("Header Files\\smithy\\client\\schema" FILES ${SMITHY_CLIENT_SCHEMA_HEADERS}) # http client conditional headers if(ENABLE_CURL_CLIENT) @@ -543,17 +548,7 @@ check_cxx_source_compiles(" return 0; }" AWS_HAS_ALIGNED_ALLOC) -add_library(${PROJECT_NAME} ${AWS_NATIVE_SDK_SRC} - include/smithy/client/schema/ShapeSerializer.h - include/smithy/client/schema/CborShapeSerializer.h - include/smithy/client/schema/Schema.h - include/smithy/client/schema/JsonShapeSerializer.h - include/smithy/client/schema/QueryShapeSerializer.h - include/smithy/client/schema/XmlShapeSerializer.h - source/smithy/client/schema/JsonShapeSerializer.cpp - include/smithy/client/schema/JsonWriteUtils.h - source/smithy/client/schema/JsonWriteUtils.cpp -) +add_library(${PROJECT_NAME} ${AWS_NATIVE_SDK_SRC}) add_library(AWS::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) target_compile_definitions(${PROJECT_NAME} PUBLIC "AWS_SDK_VERSION_MAJOR=${AWSSDK_VERSION_MAJOR}") 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 59097b64fd23..cbaba7d7eedb 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 @@ -1,8 +1,8 @@ #pragma once #include -#include #include +#include namespace smithy { namespace schema { 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 81f553719be7..d4bc17bb6e6f 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 @@ -1,17 +1,15 @@ #pragma once -#include - +#include #include - -#include "aws/core/client/AWSError.h" +#include namespace smithy { namespace schema { class JsonShapeSerializer final : public ShapeSerializer { public: - using SerializerOutcome = Aws::Utils::Outcome>; + using SerializerOutcome = Aws::Utils::Outcome>; JsonShapeSerializer(); ~JsonShapeSerializer(); diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonWriteUtils.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonWriteUtils.h index 14a37cce86d6..f1a38546a0d3 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonWriteUtils.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/JsonWriteUtils.h @@ -1,16 +1,10 @@ #pragma once - #include -namespace smithy { - namespace schema { - class JsonWriteUtils { - public: - static void WriteQuotedString(Aws::String& buf, const Aws::String& value); - private: - static const bool NEEDS_ESCAPE[128]; - static const char ESCAPE_TABLE[128]; - static const char HEX[16]; - }; - } -} \ No newline at end of file +namespace Aws { +namespace Schema { + +void WriteQuotedJsonString(Aws::String& buf, const Aws::String& value); + +} // namespace Schema +} // namespace Aws 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 c3f5e521727f..74bfe1d6e8b1 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 @@ -1,8 +1,7 @@ #pragma once -#include - #include +#include namespace smithy { namespace schema { diff --git a/src/aws-cpp-sdk-core/include/smithy/client/schema/Schema.h b/src/aws-cpp-sdk-core/include/smithy/client/schema/Schema.h index 03172f23ae9f..a8faa19266db 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/schema/Schema.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/schema/Schema.h @@ -35,12 +35,11 @@ enum class ShapeType : uint8_t { class Schema { public: Schema() = default; - Schema(const Aws::String& memberName, ShapeType type) - : m_type(type), m_memberName(memberName) {} + Schema(const Aws::String& memberName, ShapeType type) : m_type(type), m_memberName(memberName) {} ShapeType GetType() const { return m_type; } const char* GetId() const { return m_id; } - const Aws::String& GetMemberName() const { return m_memberName; } + Aws::String GetMemberName() const { return m_memberName; } int GetMemberIndex() const { return m_memberIndex; } bool IsMember() const { return !m_memberName.empty(); } 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 0362fa69bea7..c7137713aa45 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 @@ -1,8 +1,7 @@ #pragma once -#include - #include +#include namespace smithy { namespace 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 c0af9762e3dc..115304c125ee 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 @@ -20,9 +20,8 @@ using SerializerOutcome = Aws::Utils::Outcome= MAX_DEPTH) { @@ -66,7 +65,7 @@ class JsonShapeSerializer::Impl { void WriteString(const Schema& schema, const Aws::String& value) { WriteFieldName(schema); - JsonWriteUtils::WriteQuotedString(m_buf, value); + Aws::Schema::WriteQuotedJsonString(m_buf, value); } void WriteTimestamp(const Schema& schema, const DateTime& value) { @@ -154,23 +153,21 @@ class JsonShapeSerializer::Impl { } SerializerOutcome GetPayload() { - if(m_finalized || !m_errorMessage.empty()) { + if (m_finalized || !m_errorMessage.empty()) { return Aws::Client::AWSError( - Aws::Client::CoreErrors::INTERNAL_FAILURE, - "SerializationException", - !m_errorMessage.empty()?m_errorMessage:"Serializer has already been finalized", - false); + Aws::Client::CoreErrors::INTERNAL_FAILURE, "SerializationException", + !m_errorMessage.empty() ? m_errorMessage : "Serializer has already been finalized", false); } m_finalized = true; return std::move(m_buf); } -private: + private: Aws::String m_buf; int m_depth = 0; - Aws::Array m_needsComma{}; - Aws::Array m_isMap{}; - Aws::Array m_isList{}; + Aws::Array m_needsComma{}; + Aws::Array m_isMap{}; + Aws::Array m_isList{}; Aws::String m_currentMapKey; bool m_finalized = false; Aws::String m_errorMessage; @@ -184,7 +181,7 @@ class JsonShapeSerializer::Impl { } void WriteKey(const Aws::String& key) { - JsonWriteUtils::WriteQuotedString(m_buf, key); + Aws::Schema::WriteQuotedJsonString(m_buf, key); m_buf += ':'; } @@ -199,7 +196,6 @@ class JsonShapeSerializer::Impl { WriteKey(schema.GetMemberName()); } } - }; JsonShapeSerializer::JsonShapeSerializer() : m_impl(Aws::MakeUnique("JsonShapeSerializer")) {} diff --git a/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonWriteUtils.cpp b/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonWriteUtils.cpp index 069b4960fde0..f3d12a0a7758 100644 --- a/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonWriteUtils.cpp +++ b/src/aws-cpp-sdk-core/source/smithy/client/schema/JsonWriteUtils.cpp @@ -1,80 +1,75 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include #include -using namespace smithy::schema; +namespace { -const char JsonWriteUtils::HEX[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; +static const Aws::Array HEX = {{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}}; -const bool JsonWriteUtils::NEEDS_ESCAPE[128] = { - // 0x00-0x1F: control chars - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - // 0x20-0x2F: space ! " # $ % & ' ( ) * + , - . / - 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, - // 0x30-0x3F - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - // 0x40-0x4F - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - // 0x50-0x5F: P-Z [ \ ] ^ _ - 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, - // 0x60-0x6F - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - // 0x70-0x7F - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -}; +void EscapeChar(Aws::String& buf, unsigned char c) { + switch (c) { + case '\"': + buf += "\\\""; + break; + case '\\': + buf += "\\\\"; + break; + case '\b': + buf += "\\b"; + break; + case '\f': + buf += "\\f"; + break; + case '\n': + buf += "\\n"; + break; + case '\r': + buf += "\\r"; + break; + case '\t': + buf += "\\t"; + break; + default: + buf += "\\u00"; + buf += HEX[(c >> 4) & 0xF]; + buf += HEX[c & 0xF]; + break; + } +} + +bool NeedsEscape(unsigned char c) { return c < 0x20 || c == '"' || c == '\\'; } + +} // anonymous namespace -const char JsonWriteUtils::ESCAPE_TABLE[128] = { - // 0x00-0x0F - 0,0,0,0,0,0,0,0,'b','t','n',0,'f','r',0,0, - // 0x10-0x1F - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - // 0x20-0x2F - 0,0,'"',0,0,0,0,0,0,0,0,0,0,0,0,0, - // 0x30-0x3F - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - // 0x40-0x4F - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - // 0x50-0x5F - 0,0,0,0,0,0,0,0,0,0,0,0,'\\',0,0,0, - // 0x60-0x6F - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - // 0x70-0x7F - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -}; +namespace Aws { +namespace Schema { -void JsonWriteUtils::WriteQuotedString(Aws::String& buf, const Aws::String& value) { - buf += '"'; - const char* data = value.data(); - const size_t len = value.size(); +void WriteQuotedJsonString(Aws::String& buf, const Aws::String& value) { + buf += '"'; + const char* data = value.data(); + const size_t len = value.size(); - size_t i = 0; + size_t i = 0; + while (i < len) { + size_t start = i; while (i < len) { - // Fast scan: find next char that needs escaping - size_t start = i; - while (i < len) { - unsigned char c = static_cast(data[i]); - if (c < 0x80 && NEEDS_ESCAPE[c]) break; - i++; - } - // Bulk append safe run - if (i > start) { - buf.append(data + start, i - start); - } - // Escape one char - if (i < len) { - unsigned char c = static_cast(data[i]); - if (ESCAPE_TABLE[c] != 0) { - buf += '\\'; - buf += ESCAPE_TABLE[c]; - } else { - buf += '\\'; - buf += 'u'; - buf += '0'; - buf += '0'; - buf += HEX[(c >> 4) & 0xF]; - buf += HEX[c & 0xF]; - } - i++; - } + unsigned char c = static_cast(data[i]); + if (c < 0x80 && NeedsEscape(c)) break; + i++; } - buf += '"'; + if (i > start) { + buf.append(data + start, i - start); + } + if (i < len) { + EscapeChar(buf, static_cast(data[i])); + i++; + } + } + buf += '"'; } + +} // namespace Schema +} // namespace Aws From c8709632581c2d4042e9e2662bb57dceede76d38 Mon Sep 17 00:00:00 2001 From: pulimsr Date: Fri, 26 Jun 2026 14:02:29 -0400 Subject: [PATCH 7/7] Using SMITHY_API for Windows DLL export --- .../include/smithy/client/schema/CborShapeSerializer.h | 4 ++-- .../include/smithy/client/schema/JsonShapeSerializer.h | 3 ++- .../include/smithy/client/schema/QueryShapeSerializer.h | 3 ++- .../include/smithy/client/schema/XmlShapeSerializer.h | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) 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 cbaba7d7eedb..351b29b0abae 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 @@ -1,13 +1,13 @@ #pragma once -#include #include +#include #include namespace smithy { namespace schema { -class AWS_CORE_API CborShapeSerialize final : public ShapeSerializer { +class SMITHY_API CborShapeSerialize final : public ShapeSerializer { public: CborShapeSerializer(); ~CborShapeSerializer(); 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 d4bc17bb6e6f..8830bfd236c5 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 @@ -2,12 +2,13 @@ #include #include +#include #include namespace smithy { namespace schema { -class JsonShapeSerializer final : public ShapeSerializer { +class SMITHY_API JsonShapeSerializer final : public ShapeSerializer { public: using SerializerOutcome = Aws::Utils::Outcome>; JsonShapeSerializer(); 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 74bfe1d6e8b1..38c0b3962b56 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 @@ -1,12 +1,13 @@ #pragma once #include +#include #include namespace smithy { namespace schema { -class QueryShapeSerializer final : public ShapeSerializer { +class SMITHY_API QueryShapeSerializer final : public ShapeSerializer { public: QueryShapeSerializer(const Aws::String& action, const Aws::String& version); ~QueryShapeSerializer(); 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 c7137713aa45..4c894024cf75 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 @@ -1,12 +1,13 @@ #pragma once #include +#include #include namespace smithy { namespace schema { -class XmlShapeSerializer final : public ShapeSerializer { +class SMITHY_API XmlShapeSerializer final : public ShapeSerializer { public: XmlShapeSerializer(); ~XmlShapeSerializer();