From fe55115e0955be8e0b1b41c62b6f18d7549dbbb2 Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Wed, 11 Feb 2026 14:37:52 -0500 Subject: [PATCH 01/38] generate schemas (#623) --- .../go/codegen/AbstractDirectedCodegen.java | 10 +- .../smithy/go/codegen/ChainWritable.java | 10 ++ .../smithy/go/codegen/CodegenVisitor.java | 63 ++++++- .../go/codegen/DefaultTraitGenerators.java | 86 ++++++++++ .../smithy/go/codegen/GoCodegenContext.java | 7 +- .../amazon/smithy/go/codegen/GoSettings.java | 13 +- .../amazon/smithy/go/codegen/GoWriter.java | 2 +- .../smithy/go/codegen/OperationGenerator.java | 38 ++--- .../smithy/go/codegen/SchemaGenerator.java | 87 ++++++++++ .../go/codegen/SimpleTraitGenerator.java | 48 ++++++ .../smithy/go/codegen/SmithyGoDependency.java | 1 + .../smithy/go/codegen/StructureGenerator.java | 29 +++- .../smithy/go/codegen/TraitGenerator.java | 7 + .../go/codegen/UnsupportedShapeException.java | 10 ++ .../amazon/smithy/go/codegen/Writable.java | 12 ++ .../smithy/go/codegen/serde/SerdeUtil.java | 3 + .../smithy/go/codegen/util/ShapeUtil.java | 11 ++ schema.go | 156 ++++++++++++++++++ trait.go | 8 + traits/http.go | 49 ++++++ traits/serde.go | 54 ++++++ traits/traits.go | 48 ++++++ 22 files changed, 709 insertions(+), 43 deletions(-) create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SimpleTraitGenerator.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TraitGenerator.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnsupportedShapeException.java create mode 100644 schema.go create mode 100644 trait.go create mode 100644 traits/http.go create mode 100644 traits/serde.go create mode 100644 traits/traits.go diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/AbstractDirectedCodegen.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/AbstractDirectedCodegen.java index 68f9e7e6a..8043d8bb5 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/AbstractDirectedCodegen.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/AbstractDirectedCodegen.java @@ -89,12 +89,9 @@ public void generateStructure(GenerateStructureDirective new StructureGenerator( - directive.model(), - directive.symbolProvider(), + directive.context(), writer, - directive.service(), directive.shape(), - directive.symbolProvider().toSymbol(directive.shape()), null ).run() ); @@ -105,12 +102,9 @@ public void generateError(GenerateErrorDirective d var delegator = directive.context().writerDelegator(); delegator.useShapeWriter(directive.shape(), writer -> new StructureGenerator( - directive.model(), - directive.symbolProvider(), + directive.context(), writer, - directive.service(), directive.shape(), - directive.symbolProvider().toSymbol(directive.shape()), null ).run() ); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ChainWritable.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ChainWritable.java index 1cd7ec7e7..4055cb818 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ChainWritable.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ChainWritable.java @@ -11,6 +11,8 @@ * Chains together multiple Writables that can be composed into one Writable. */ public final class ChainWritable { + // FUTURE: move statics to Writable, make this file- or package-private + private final List writables; public ChainWritable() { @@ -29,6 +31,14 @@ public static ChainWritable of(Collection writables) { return chain; } + public static ChainWritable of(Collection values, Function mapper) { + var chain = new ChainWritable(); + chain.writables.addAll(values.stream() + .map(mapper) + .toList()); + return chain; + } + public boolean isEmpty() { return writables.isEmpty(); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java index 201459f00..ef8b73d07 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java @@ -15,10 +15,13 @@ package software.amazon.smithy.go.codegen; +import static java.util.stream.Collectors.toSet; + import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -29,7 +32,6 @@ import java.util.stream.Collectors; import software.amazon.smithy.build.FileManifest; import software.amazon.smithy.build.PluginContext; -import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.codegen.core.SymbolDependency; import software.amazon.smithy.codegen.core.SymbolProvider; import software.amazon.smithy.go.codegen.GoSettings.ArtifactType; @@ -45,11 +47,13 @@ import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.shapes.ShapeType; import software.amazon.smithy.model.shapes.ShapeVisitor; import software.amazon.smithy.model.shapes.StringShape; import software.amazon.smithy.model.shapes.StructureShape; import software.amazon.smithy.model.shapes.UnionShape; import software.amazon.smithy.model.traits.EnumTrait; +import software.amazon.smithy.model.traits.ErrorTrait; import software.amazon.smithy.model.transform.ModelTransformer; import software.amazon.smithy.utils.OptionalUtils; import software.amazon.smithy.utils.SmithyInternalApi; @@ -219,7 +223,7 @@ void execute() { TopDownIndex.of(model).getContainedOperations(service) .forEach(eventStreamGenerator::generateOperationEventStreamStructure); - if (protocolGenerator != null) { + if (!settings.useExperimentalSerde() && protocolGenerator != null) { LOGGER.info("Generating serde for protocol " + protocolGenerator.getProtocol() + " on " + service.getId()); ProtocolGenerator.GenerationContext.Builder contextBuilder = ProtocolGenerator.GenerationContext.builder() .protocolName(protocolGenerator.getProtocolName()) @@ -251,6 +255,55 @@ void execute() { }); } + LOGGER.info("Generating protocol " + protocolGenerator.getProtocol() + + " unit tests for " + service.getId()); + writers.useFileWriter("protocol_test.go", settings.getModuleName(), writer -> { + protocolGenerator.generateProtocolTests(contextBuilder.writer(writer).build()); + }); + + protocolDocumentGenerator.generateInternalDocumentTypes(protocolGenerator, contextBuilder.build()); + } + + if (settings.useExperimentalSerde()) { + var walker = new Walker(model); + var operations = TopDownIndex.of(model).getContainedOperations(service); + + var shapes = new HashSet(); + shapes.addAll(operations.stream() + .map(it -> model.expectShape(it.getInputShape())) + .flatMap(it -> walker.walkShapes(it).stream()) + .collect(toSet())); + shapes.addAll(operations.stream() + .map(it -> model.expectShape(it.getOutputShape())) + .flatMap(it -> walker.walkShapes(it).stream()) + .collect(toSet())); + shapes.addAll(model.getStructureShapesWithTrait(ErrorTrait.class).stream() + .flatMap(it -> walker.walkShapes(it).stream()) + .collect(toSet())); + + var sortedShapes = new ArrayList<>(shapes.stream() + .sorted() + .filter(it -> it.getType() != ShapeType.MEMBER) + .toList()); + + sortedShapes.add(StructureShape.builder().id("smithy.api#Unit").build()); + + ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", + Writable.map(sortedShapes, it -> new SchemaGenerator(ctx, it), true)); + } + + // TODO: With serde/schema decoupling, protocol generators are going away. Endpoint/auth resolution is going to + // need to be decoupled from that and I don't really know how yet. Probably just separate integrations / + // integration hooks. + if (protocolGenerator != null) { + ProtocolGenerator.GenerationContext.Builder contextBuilder = ProtocolGenerator.GenerationContext.builder() + .protocolName(protocolGenerator.getProtocolName()) + .integrations(integrations) + .model(model) + .service(service) + .settings(settings) + .symbolProvider(symbolProvider) + .delegator(writers); writers.useFileWriter("endpoints.go", settings.getModuleName(), writer -> { ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); protocolGenerator.generateEndpointResolution(context); @@ -297,9 +350,9 @@ public Void structureShape(StructureShape shape) { if (shape.getId().getNamespace().equals(CodegenUtils.getSyntheticTypeNamespace())) { return null; } - Symbol symbol = symbolProvider.toSymbol(shape); - writers.useShapeWriter(shape, writer -> new StructureGenerator( - model, symbolProvider, writer, service, shape, symbol, protocolGenerator).run()); + writers.useShapeWriter(shape, writer -> + new StructureGenerator(ctx, writer, shape, protocolGenerator).run()); + return null; } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java new file mode 100644 index 000000000..667bc5987 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java @@ -0,0 +1,86 @@ +package software.amazon.smithy.go.codegen; + +import static software.amazon.smithy.go.codegen.SmithyGoDependency.SMITHY_TRAITS; + +import java.util.HashMap; +import java.util.Map; +import software.amazon.smithy.aws.traits.protocols.AwsQueryErrorTrait; +import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.EventHeaderTrait; +import software.amazon.smithy.model.traits.EventPayloadTrait; +import software.amazon.smithy.model.traits.HostLabelTrait; +import software.amazon.smithy.model.traits.HttpErrorTrait; +import software.amazon.smithy.model.traits.HttpHeaderTrait; +import software.amazon.smithy.model.traits.HttpLabelTrait; +import software.amazon.smithy.model.traits.HttpPayloadTrait; +import software.amazon.smithy.model.traits.HttpPrefixHeadersTrait; +import software.amazon.smithy.model.traits.HttpQueryParamsTrait; +import software.amazon.smithy.model.traits.HttpQueryTrait; +import software.amazon.smithy.model.traits.HttpResponseCodeTrait; +import software.amazon.smithy.model.traits.JsonNameTrait; +import software.amazon.smithy.model.traits.MediaTypeTrait; +import software.amazon.smithy.model.traits.SensitiveTrait; +import software.amazon.smithy.model.traits.StreamingTrait; +import software.amazon.smithy.model.traits.TimestampFormatTrait; +import software.amazon.smithy.model.traits.XmlAttributeTrait; +import software.amazon.smithy.model.traits.XmlFlattenedTrait; +import software.amazon.smithy.model.traits.XmlNameTrait; +import software.amazon.smithy.model.traits.XmlNamespaceTrait; +import software.amazon.smithy.rulesengine.traits.ContextParamTrait; + +public class DefaultTraitGenerators { + private static final Map GENERATORS = new HashMap<>(); + + static { + // Documentation traits + GENERATORS.put(SensitiveTrait.ID, new SimpleTraitGenerator(SMITHY_TRAITS.struct("Sensitive"))); + + // Serialization and Protocol traits + GENERATORS.put(JsonNameTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("JSONName"), + "Name", JsonNameTrait::getValue)); + GENERATORS.put(MediaTypeTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("MediaType"), + "Type", MediaTypeTrait::getValue)); + GENERATORS.put(TimestampFormatTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("TimestampFormat"), + "Format", TimestampFormatTrait::getValue)); + GENERATORS.put(XmlAttributeTrait.ID, new SimpleTraitGenerator(SMITHY_TRAITS.struct("XMLAttribute"))); + GENERATORS.put(XmlFlattenedTrait.ID, new SimpleTraitGenerator(SMITHY_TRAITS.struct("XMLFlattened"))); + GENERATORS.put(XmlNameTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("XMLName"), + "Name", XmlNameTrait::getValue)); + GENERATORS.put(XmlNamespaceTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("XMLNamespace"), + "URI", XmlNamespaceTrait::getUri, + "Prefix", XmlNamespaceTrait::getPrefix)); + + // Streaming + GENERATORS.put(EventHeaderTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("EventHeader"))); + GENERATORS.put(EventPayloadTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("EventPayload"))); + GENERATORS.put(StreamingTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("Streaming"))); + + // HTTP bindings + GENERATORS.put(HttpHeaderTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HTTPHeader"), + "Name", HttpHeaderTrait::getValue)); + GENERATORS.put(HttpLabelTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HTTPLabel"))); + GENERATORS.put(HttpPayloadTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HTTPPayload"))); + GENERATORS.put(HttpPrefixHeadersTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HTTPPrefixHeaders"), + "Prefix", HttpPrefixHeadersTrait::getValue)); + GENERATORS.put(HttpQueryTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HTTPQuery"), + "Name", HttpQueryTrait::getValue)); + GENERATORS.put(HttpQueryParamsTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HTTPQueryParams"))); + GENERATORS.put(HttpResponseCodeTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HTTPResponseCode"))); + + // Endpoint Traits + GENERATORS.put(HostLabelTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HostLabel"))); + + // Other traits + GENERATORS.put(ContextParamTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("ContextParam"))); + GENERATORS.put(AwsQueryErrorTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("AWSQueryError"), + "ErrorCode", AwsQueryErrorTrait::getCode, + "StatusCode", AwsQueryErrorTrait::getHttpResponseCode)); + } + + public static TraitGenerator forTrait(ShapeId id) { + return GENERATORS.get(id); + } + + private DefaultTraitGenerators() { + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoCodegenContext.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoCodegenContext.java index 131fb8369..6f4a197e1 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoCodegenContext.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoCodegenContext.java @@ -22,6 +22,7 @@ import software.amazon.smithy.codegen.core.WriterDelegator; import software.amazon.smithy.go.codegen.integration.GoIntegration; import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.utils.SmithyInternalApi; @SmithyInternalApi @@ -32,4 +33,8 @@ public record GoCodegenContext( FileManifest fileManifest, WriterDelegator writerDelegator, List integrations -) implements CodegenContext {} +) implements CodegenContext { + public ServiceShape service() { + return settings.getService(model); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java index 7d6ca3201..d68af2440 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java @@ -55,6 +55,7 @@ public final class GoSettings { private static final String MODULE_DESCRIPTION = "moduleDescription"; private static final String MODULE_VERSION = "moduleVersion"; private static final String GENERATE_GO_MOD = "generateGoMod"; + private static final String USE_EXPERIMENTAL_SERDE = "useExperimentalSerde"; private static final String GO_DIRECTIVE = "goDirective"; private ShapeId service; @@ -62,6 +63,7 @@ public final class GoSettings { private String moduleDescription = ""; private String moduleVersion; private Boolean generateGoMod = false; + private Boolean useExperimentalSerde = false; private String goDirective = GoModuleInfo.DEFAULT_GO_DIRECTIVE; private ShapeId protocol; private ArtifactType artifactType; @@ -86,7 +88,7 @@ public static GoSettings from(ObjectNode config) { public static GoSettings from(ObjectNode config, ArtifactType artifactType) { GoSettings settings = new GoSettings(); config.warnIfAdditionalProperties( - Arrays.asList(SERVICE, MODULE_NAME, MODULE_DESCRIPTION, MODULE_VERSION, GENERATE_GO_MOD, GO_DIRECTIVE)); + Arrays.asList(SERVICE, MODULE_NAME, MODULE_DESCRIPTION, MODULE_VERSION, GENERATE_GO_MOD, USE_EXPERIMENTAL_SERDE, GO_DIRECTIVE)); settings.setArtifactType(artifactType); settings.setService(config.expectStringMember(SERVICE).expectShapeId()); settings.setModuleName(config.expectStringMember(MODULE_NAME).getValue()); @@ -94,6 +96,7 @@ public static GoSettings from(ObjectNode config, ArtifactType artifactType) { MODULE_DESCRIPTION, settings.getModuleName() + " client")); settings.setModuleVersion(config.getStringMemberOrDefault(MODULE_VERSION, null)); settings.setGenerateGoMod(config.getBooleanMemberOrDefault(GENERATE_GO_MOD, false)); + settings.setUseExperimentalSerde(config.getBooleanMemberOrDefault(USE_EXPERIMENTAL_SERDE, false)); settings.setGoDirective(config.getStringMemberOrDefault(GO_DIRECTIVE, GoModuleInfo.DEFAULT_GO_DIRECTIVE)); return settings; } @@ -217,6 +220,14 @@ public void setGenerateGoMod(Boolean generateGoMod) { this.generateGoMod = Objects.requireNonNull(generateGoMod); } + public Boolean useExperimentalSerde() { + return useExperimentalSerde; + } + + public void setUseExperimentalSerde(Boolean value) { + this.useExperimentalSerde = Objects.requireNonNull(value); + } + /** * Gets the optional Go directive for the module that will be generated. * diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoWriter.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoWriter.java index 8e4e01c0b..9a66f0056 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoWriter.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoWriter.java @@ -24,6 +24,7 @@ import java.util.function.Consumer; import java.util.logging.Logger; import java.util.regex.Pattern; +import java.util.stream.Collectors; import software.amazon.smithy.codegen.core.CodegenException; import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.codegen.core.SymbolContainer; @@ -997,5 +998,4 @@ public String apply(Object type, String indent) { return ""; } } - } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java index 0076d3acf..3d3b194ec 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java @@ -133,9 +133,8 @@ public void run() { // Write out the input and output structures. These are written out here to prevent naming conflicts with other // shapes in the model. - new StructureGenerator(model, symbolProvider, writer, service, inputShape, inputSymbol, protocolGenerator) - .renderStructure(() -> { - }, true); + new StructureGenerator(ctx, writer, inputShape, protocolGenerator) + .renderStructure(() -> {}, true); var rulesTrait = service.getTrait(EndpointRuleSetTrait.class); var bddTrait = service.getTrait(EndpointBddTrait.class); @@ -149,17 +148,14 @@ public void run() { .build(); StructureShape structOutputShape; - Symbol structOutputSymbol; - + if (EventStreamGenerator.isV2EventStream(model, operation)) { List onlyEventStreamMembers = outputShape.members().stream().filter(member -> StreamingTrait.isEventStream(model, member)).toList(); structOutputShape = buildShape(onlyEventStreamMembers); - structOutputSymbol = symbolProvider.toSymbol(outputShape); } else { structOutputShape = outputShape; - structOutputSymbol = outputSymbol; } - new StructureGenerator(model, symbolProvider, writer, service, structOutputShape, structOutputSymbol, protocolGenerator) + new StructureGenerator(ctx, writer, structOutputShape, protocolGenerator) .renderStructure(() -> { if (outputShape.getMemberNames().size() != 0) { writer.write(""); @@ -278,17 +274,21 @@ private void generateOperationProtocolMiddlewareAdders() { return err }"""); - // Add request serializer middleware - String serializerMiddlewareName = ProtocolGenerator.getSerializeMiddlewareName( - operation.getId(), service, protocolGenerator.getProtocolName()); - writer.write("err = stack.Serialize.Add(&$L{}, middleware.After)", serializerMiddlewareName); - writer.write("if err != nil { return err }"); - - // Adds response deserializer middleware - String deserializerMiddlewareName = ProtocolGenerator.getDeserializeMiddlewareName( - operation.getId(), service, protocolGenerator.getProtocolName()); - writer.write("err = stack.Deserialize.Add(&$L{}, middleware.After)", deserializerMiddlewareName); - writer.write("if err != nil { return err }"); + if (!ctx.settings().useExperimentalSerde()) { + // Add request serializer middleware + String serializerMiddlewareName = ProtocolGenerator.getSerializeMiddlewareName( + operation.getId(), service, protocolGenerator.getProtocolName()); + writer.write("err = stack.Serialize.Add(&$L{}, middleware.After)", serializerMiddlewareName); + writer.write("if err != nil { return err }"); + + // Adds response deserializer middleware + String deserializerMiddlewareName = ProtocolGenerator.getDeserializeMiddlewareName( + operation.getId(), service, protocolGenerator.getProtocolName()); + writer.write("err = stack.Deserialize.Add(&$L{}, middleware.After)", deserializerMiddlewareName); + writer.write("if err != nil { return err }"); + } else { + // TODO + } // FUTURE: retry middleware should be at the front of finalize, right now it's added by the SDK writer.write(""" diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java new file mode 100644 index 000000000..1fb7dd2aa --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java @@ -0,0 +1,87 @@ +package software.amazon.smithy.go.codegen; + +import static software.amazon.smithy.go.codegen.GoWriter.emptyGoTemplate; +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + +import java.util.Map; +import software.amazon.smithy.go.codegen.util.ShapeUtil; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.traits.Trait; +import software.amazon.smithy.utils.SmithyInternalApi; +import software.amazon.smithy.utils.StringUtils; + +@SmithyInternalApi +public class SchemaGenerator implements Writable { + private final GoCodegenContext ctx; + private final Shape shape; + + public SchemaGenerator(GoCodegenContext ctx, Shape shape) { + this.ctx = ctx; + this.shape = shape; + } + + public static String getSchemaName(Shape shape) { + var name = shape.getId().getName(); + return ShapeUtil.isExported(shape) + ? name + : "_" + name; + } + + public static String getMemberSchemaName(Shape shape, MemberShape member) { + return String.format("%s_%s", getSchemaName(shape), member.getMemberName()); + } + + @Override + public void accept(GoWriter writer) { + writer.addUseImports(SmithyGoDependency.SMITHY); + writer.writeGoTemplate(""" + var $ident:L = smithy.NewSchema($id:S, smithy.ShapeType$type:L, + $memberOpts:W + smithy.WithTraits( + $traitOpts:W + ), + ) + $members:W + """, + Map.of( + "ident", getSchemaName(shape), + "id", shape.getId().toString(), + "type", StringUtils.capitalize(shape.getType().toString()), + "members", Writable.map(shape.members(), this::renderMemberIdent), + "memberOpts", Writable.map(shape.members(), this::renderMemberOpt), + "traitOpts", Writable.map(shape.getAllTraits().values(), this::renderTraitOpt, true) + )); + } + + private Writable renderMemberIdent(MemberShape member) { + var memberName = member.getMemberName(); + return goTemplate(""" + var $ident:L = $schema:L.Member($name:S) + """, + Map.of( + "ident", getMemberSchemaName(shape, member), + "schema", getSchemaName(shape), + "name", memberName + )); + } + + private Writable renderMemberOpt(MemberShape member) { + var target = ctx.model().expectShape(member.getTarget()); + + return goTemplate("smithy.WithMember($name:S, $target:L, $traits:W),", + Map.of( + "name", member.getMemberName(), + "target", getSchemaName(target), + "traits", Writable.map(member.getAllTraits().values(), this::renderTraitOpt) + )); + } + + private Writable renderTraitOpt(Trait trait) { + // FUTURE: load additional generators through GoIntegration + var generator = DefaultTraitGenerators.forTrait(trait.toShapeId()); + return generator != null + ? goTemplate("$W,", generator.render(trait)) + : emptyGoTemplate(); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SimpleTraitGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SimpleTraitGenerator.java new file mode 100644 index 000000000..49e22d6ae --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SimpleTraitGenerator.java @@ -0,0 +1,48 @@ +package software.amazon.smithy.go.codegen; + +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Function; +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.model.traits.Trait; + +class SimpleTraitGenerator implements TraitGenerator { + private final Symbol symbol; + private final Map> mappings = new LinkedHashMap<>(); + + public SimpleTraitGenerator(Symbol symbol) { + this.symbol = symbol; + } + + public SimpleTraitGenerator(Symbol symbol, + String k1, Function v1) { + this.symbol = symbol; + + mappings.put(k1, v1); + } + + public SimpleTraitGenerator(Symbol symbol, + String k1, Function v1, + String k2, Function v2) { + this.symbol = symbol; + + mappings.put(k1, v1); + mappings.put(k2, v2); + } + + @Override + public Writable render(Trait trait) { + return goTemplate("&$T{$W}", symbol, Writable.map(mappings.entrySet(), entry -> { + var value = entry.getValue().apply((T) trait); + return goTemplate("$L: $L,", entry.getKey(), formatValue(value)); + })); + } + + private String formatValue(Object value) { + return value instanceof String + ? "\"" + value + "\"" + : value.toString(); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java index 2f5bbbb80..69c8d2fd1 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java @@ -51,6 +51,7 @@ public final class SmithyGoDependency { public static final GoDependency SLICES = stdlib("slices"); public static final GoDependency SMITHY = smithy(null, "smithy"); + public static final GoDependency SMITHY_TRAITS = smithy("traits", "smithytraits"); public static final GoDependency SMITHY_TRANSPORT = smithy("transport", "smithytransport"); public static final GoDependency SMITHY_HTTP_TRANSPORT = smithy("transport/http", "smithyhttp"); public static final GoDependency SMITHY_MIDDLEWARE = smithy("middleware"); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java index bd54b210f..97dfedbdb 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java @@ -15,14 +15,20 @@ package software.amazon.smithy.go.codegen; +import java.util.Comparator; import java.util.Map; import java.util.Set; import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.codegen.core.SymbolProvider; import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; +import software.amazon.smithy.go.codegen.knowledge.GoPointableIndex; +import software.amazon.smithy.go.codegen.util.ShapeUtil; import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.shapes.CollectionShape; +import software.amazon.smithy.model.shapes.MapShape; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.StructureShape; import software.amazon.smithy.model.traits.ErrorTrait; import software.amazon.smithy.model.traits.StreamingTrait; @@ -49,23 +55,26 @@ public final class StructureGenerator implements Runnable { private final Symbol symbol; private final ServiceShape service; private final ProtocolGenerator protocolGenerator; + private final boolean useExperimentalSerde; + private final GoCodegenContext ctx; + private final GoPointableIndex nilIndex; public StructureGenerator( - Model model, - SymbolProvider symbolProvider, + GoCodegenContext ctx, GoWriter writer, - ServiceShape service, StructureShape shape, - Symbol symbol, ProtocolGenerator protocolGenerator ) { - this.model = model; - this.symbolProvider = symbolProvider; + this.ctx = ctx; + this.model = ctx.model(); + this.symbolProvider = ctx.symbolProvider(); this.writer = writer; - this.service = service; + this.service = ctx.service(); this.shape = shape; - this.symbol = symbol; + this.symbol = ctx.symbolProvider().toSymbol(shape); this.protocolGenerator = protocolGenerator; + this.useExperimentalSerde = ctx.settings().useExperimentalSerde(); + this.nilIndex = GoPointableIndex.of(model); } @Override @@ -129,6 +138,10 @@ public void renderStructure(Runnable runnable, boolean isInputStructure) { writer.write("$L", ProtocolDocumentGenerator.NO_DOCUMENT_SERDE_TYPE_NAME); writer.closeBlock("}").write(""); + + if (useExperimentalSerde) { + // TODO generateSerializers(); + } } /** diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TraitGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TraitGenerator.java new file mode 100644 index 000000000..b95585d43 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TraitGenerator.java @@ -0,0 +1,7 @@ +package software.amazon.smithy.go.codegen; + +import software.amazon.smithy.model.traits.Trait; + +public interface TraitGenerator { + Writable render(Trait trait); +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnsupportedShapeException.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnsupportedShapeException.java new file mode 100644 index 000000000..abc98135f --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnsupportedShapeException.java @@ -0,0 +1,10 @@ +package software.amazon.smithy.go.codegen; + +import software.amazon.smithy.codegen.core.CodegenException; +import software.amazon.smithy.model.shapes.ShapeType; + +public class UnsupportedShapeException extends CodegenException { + public UnsupportedShapeException(ShapeType type) { + super("unsupported shape type: " + type); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/Writable.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/Writable.java index 502bcbe6e..4cc6a3307 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/Writable.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/Writable.java @@ -1,6 +1,18 @@ package software.amazon.smithy.go.codegen; +import java.util.Collection; import java.util.function.Consumer; +import java.util.function.Function; public interface Writable extends Consumer { + static Writable map(Collection items, Function mapper, boolean writeNewlines) { + return ChainWritable.of(items.stream() + .map(mapper) + .toList() + ).compose(writeNewlines); + } + + static Writable map(Collection items, Function mapper) { + return map(items, mapper, false); + } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde/SerdeUtil.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde/SerdeUtil.java index 474d77e3a..8430eb058 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde/SerdeUtil.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde/SerdeUtil.java @@ -36,6 +36,9 @@ import software.amazon.smithy.model.traits.StreamingTrait; import software.amazon.smithy.utils.SmithyInternalApi; +/** + * WARNING 1/29/26: AVOID any new usages of these. + */ @SmithyInternalApi public final class SerdeUtil { private SerdeUtil() {} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java index 1a561c1ca..7224d1614 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java @@ -15,14 +15,18 @@ package software.amazon.smithy.go.codegen.util; +import java.util.Set; import software.amazon.smithy.codegen.core.CodegenException; import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.knowledge.TopDownIndex; +import software.amazon.smithy.model.neighbor.Walker; import software.amazon.smithy.model.shapes.BooleanShape; import software.amazon.smithy.model.shapes.CollectionShape; import software.amazon.smithy.model.shapes.IntegerShape; import software.amazon.smithy.model.shapes.ListShape; import software.amazon.smithy.model.shapes.MapShape; import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeType; import software.amazon.smithy.model.shapes.StringShape; public final class ShapeUtil { @@ -40,6 +44,13 @@ public final class ShapeUtil { private ShapeUtil() {} + public static boolean isExported(Shape shape) { + return switch (shape.getType()) { + case STRUCTURE, UNION, ENUM -> true; + default -> false; + }; + } + public static ListShape listOf(Shape member) { return ListShape.builder() .id("smithy.go.synthetic#" + member.getId().getName() + "List") diff --git a/schema.go b/schema.go new file mode 100644 index 000000000..1f140f3d6 --- /dev/null +++ b/schema.go @@ -0,0 +1,156 @@ +package smithy + +import ( + "maps" + "strings" +) + +// ShapeType is a type of Smithy shape. +// See https://smithy.io/2.0/spec/idl.html#defining-shapes. +type ShapeType int + +// Enumerates ShapeType per the Smithy IDL. +const ( + ShapeTypeBlob ShapeType = iota + ShapeTypeBoolean + ShapeTypeString + ShapeTypeTimestamp + ShapeTypeByte + ShapeTypeShort + ShapeTypeInteger + ShapeTypeLong + ShapeTypeFloat + ShapeTypeDocument + ShapeTypeDouble + ShapeTypeBigDecimal + ShapeTypeBigInteger + ShapeTypeEnum + ShapeTypeIntEnum + ShapeTypeList + ShapeTypeSet + ShapeTypeMap + ShapeTypeStructure + ShapeTypeUnion + ShapeTypeMember + ShapeTypeService + ShapeTypeResource + ShapeTypeOperation +) + +// ShapeID fields of a Smithy shape ID. +type ShapeID struct { + Namespace, Name, Member string +} + +func stoid(s string) ShapeID { + ns, n, _ := strings.Cut(s, "#") + n, m, _ := strings.Cut(n, "$") + return ShapeID{ns, n, m} +} + +// Schema encodes information about a shape from a Smithy model. +// +// Generated clients use schemas at runtime to dynamically (de)serialize +// request/responses. +type Schema struct { + id ShapeID + typ ShapeType + + members map[string]*Schema // member name -> schema + traits map[string]Trait // trait ID -> trait +} + +// SchemaOptions configures a new Schema. +type SchemaOptions struct { + members []*Schema + traits []Trait +} + +// WithMember adds a member targeting the given Schema. +// +// Traits provided for the member here override any traits on the target if +// there is collision. +func WithMember(name string, target *Schema, traits ...Trait) func(*SchemaOptions) { + return func(o *SchemaOptions) { + m := &Schema{ + id: ShapeID{Member: name}, + typ: target.typ, + members: target.members, + traits: maps.Clone(target.traits), + } + + for _, t := range traits { + m.traits[t.TraitID()] = t + } + + o.members = append(o.members, m) + } +} + +// WithTraits adds traits to the Schema. +func WithTraits(traits ...Trait) func(*SchemaOptions) { + return func(o *SchemaOptions) { + o.traits = append(o.traits, traits...) + } +} + +// NewSchema returns a schema with the provided members and traits. +// +// Generated clients include schemas for every shape that needs to be +// (de)serialized as part of a service operation in a schemas/ package. +func NewSchema(id string, typ ShapeType, opts ...func(*SchemaOptions)) *Schema { + var o SchemaOptions + for _, opt := range opts { + opt(&o) + } + + sid := stoid(id) + members := make(map[string]*Schema, len(o.members)) + for _, m := range o.members { + m.id.Namespace = sid.Namespace + m.id.Name = sid.Name + members[m.id.Member] = m + } + + traits := make(map[string]Trait, len(o.traits)) + for _, t := range o.traits { + traits[t.TraitID()] = t + } + + return &Schema{ + id: sid, + typ: typ, + members: members, + traits: traits, + } +} + +// ID returns the shape ID for this schema as it appears in the original +// Smithy model. +func (s *Schema) ID() ShapeID { + return s.id +} + +// Type returns the schema's type. +func (s *Schema) Type() ShapeType { + return s.typ +} + +// Member returns the named member from the schema. +func (s *Schema) Member(name string) *Schema { + m, _ := s.members[name] + return m +} + +// Trait returns the target trait on the schema if it exists. +func SchemaTrait[T Trait](s *Schema) (T, bool) { + var trait T + + opaque, ok := s.traits[trait.TraitID()] + if !ok { + return trait, false + } + + tt, ok := opaque.(T) + return tt, ok +} diff --git a/trait.go b/trait.go new file mode 100644 index 000000000..3e9768737 --- /dev/null +++ b/trait.go @@ -0,0 +1,8 @@ +package smithy + +// Trait represents a trait applied to a shape in a Smithy model. Traits +// related to (de)serialization are included in code-generated Schemas for the +// client. +type Trait interface { + TraitID() string +} diff --git a/traits/http.go b/traits/http.go new file mode 100644 index 000000000..f9a544f8e --- /dev/null +++ b/traits/http.go @@ -0,0 +1,49 @@ +package traits + +// HTTPHeader represents smithy.api#httpHeader. +type HTTPHeader struct { + Name string +} + +// TraitID identifies the trait. +func (*HTTPHeader) TraitID() string { return "smithy.api#httpHeader" } + +// HTTPLabel represents smithy.api#httpLabel. +type HTTPLabel struct{} + +// TraitID identifies the trait. +func (*HTTPLabel) TraitID() string { return "smithy.api#httpLabel" } + +// HTTPPayload represents smithy.api#httpPayload. +type HTTPPayload struct{} + +// TraitID identifies the trait. +func (*HTTPPayload) TraitID() string { return "smithy.api#httpPayload" } + +// HTTPPrefixHeaders represents smithy.api#httpPrefixHeaders. +type HTTPPrefixHeaders struct { + Prefix string +} + +// TraitID identifies the trait. +func (*HTTPPrefixHeaders) TraitID() string { return "smithy.api#httpPrefixHeaders" } + +// HTTPQuery represents smithy.api#httpQuery. +type HTTPQuery struct { + Name string +} + +// TraitID identifies the trait. +func (*HTTPQuery) TraitID() string { return "smithy.api#httpQuery" } + +// HTTPQueryParams represents smithy.api#httpQueryParams. +type HTTPQueryParams struct{} + +// TraitID identifies the trait. +func (*HTTPQueryParams) TraitID() string { return "smithy.api#httpQueryParams" } + +// HTTPResponseCode represents smithy.api#httpResponseCode. +type HTTPResponseCode struct{} + +// TraitID identifies the trait. +func (*HTTPResponseCode) TraitID() string { return "smithy.api#httpResponseCode" } diff --git a/traits/serde.go b/traits/serde.go new file mode 100644 index 000000000..d4a2c0441 --- /dev/null +++ b/traits/serde.go @@ -0,0 +1,54 @@ +package traits + +// JSONName represents smithy.api#jsonName. +type JSONName struct { + Name string +} + +// TraitID identifies the trait. +func (*JSONName) TraitID() string { return "smithy.api#jsonName" } + +// MediaType represents smithy.api#mediaType. +type MediaType struct { + Type string +} + +// TraitID identifies the trait. +func (*MediaType) TraitID() string { return "smithy.api#mediaType" } + +// TimestampFormat represents smithy.api#timestampFormat. +type TimestampFormat struct { + Format string +} + +// TraitID identifies the trait. +func (*TimestampFormat) TraitID() string { return "smithy.api#timestampFormat" } + +// XMLAttribute represents smithy.api#xmlAttribute. +type XMLAttribute struct{} + +// TraitID identifies the trait. +func (*XMLAttribute) TraitID() string { return "smithy.api#xmlAttribute" } + +// XMLFlattened represents smithy.api#xmlFlattened. +type XMLFlattened struct{} + +// TraitID identifies the trait. +func (*XMLFlattened) TraitID() string { return "smithy.api#xmlFlattened" } + +// XMLName represents smithy.api#xmlName. +type XMLName struct { + Name string +} + +// TraitID identifies the trait. +func (*XMLName) TraitID() string { return "smithy.api#xmlName" } + +// XMLNamespace represents smithy.api#xmlNamespace. +type XMLNamespace struct { + URI string + Prefix string +} + +// TraitID identifies the trait. +func (*XMLNamespace) TraitID() string { return "smithy.api#xmlNamespace" } diff --git a/traits/traits.go b/traits/traits.go new file mode 100644 index 000000000..9d9cb9b83 --- /dev/null +++ b/traits/traits.go @@ -0,0 +1,48 @@ +// Package traits defines representations of Smithy IDL traits that appear in +// code-generated schemas. +package traits + +// Sensitive represents smithy.api#sensitive. +type Sensitive struct{} + +// TraitID identifies the trait. +func (*Sensitive) TraitID() string { return "smithy.api#sensitive" } + +// EventHeader represents smithy.api#eventHeader. +type EventHeader struct{} + +// TraitID identifies the trait. +func (*EventHeader) TraitID() string { return "smithy.api#eventHeader" } + +// EventPayload represents smithy.api#eventPayload. +type EventPayload struct{} + +// TraitID identifies the trait. +func (*EventPayload) TraitID() string { return "smithy.api#eventPayload" } + +// Streaming represents smithy.api#streaming. +type Streaming struct{} + +// TraitID identifies the trait. +func (*Streaming) TraitID() string { return "smithy.api#streaming" } + +// HostLabel represents smithy.api#hostLabel. +type HostLabel struct{} + +// TraitID identifies the trait. +func (*HostLabel) TraitID() string { return "smithy.api#hostLabel" } + +// ContextParam represents smithy.rules#contextParam. +type ContextParam struct{} + +// TraitID identifies the trait. +func (*ContextParam) TraitID() string { return "smithy.rules#contextParam" } + +// AWSQueryError represents aws.protocols#awsQueryError. +type AWSQueryError struct { + ErrorCode string + StatusCode int +} + +// TraitID identifies the trait. +func (*AWSQueryError) TraitID() string { return "aws.protocols#awsQueryError" } From d4c29354f279f406b54e928ceb6a66832ea2c97f Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Wed, 11 Feb 2026 16:52:04 -0500 Subject: [PATCH 02/38] reduce the code size of schemas substantially --- .../smithy/go/codegen/SchemaGenerator.java | 69 +++++++++---- schema.go | 96 ++++--------------- 2 files changed, 68 insertions(+), 97 deletions(-) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java index 1fb7dd2aa..6883b6220 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java @@ -36,28 +36,52 @@ public static String getMemberSchemaName(Shape shape, MemberShape member) { public void accept(GoWriter writer) { writer.addUseImports(SmithyGoDependency.SMITHY); writer.writeGoTemplate(""" - var $ident:L = smithy.NewSchema($id:S, smithy.ShapeType$type:L, - $memberOpts:W - smithy.WithTraits( - $traitOpts:W - ), - ) - $members:W + var $ident:L = &smithy.Schema{ + ID: smithy.ShapeID{ + Namespace: $namespace:S, + Name: $name:S, + }, + Type: smithy.ShapeType$type:L, + $members:W + $traits:W + } + $memberVars:W """, Map.of( "ident", getSchemaName(shape), - "id", shape.getId().toString(), + "namespace", shape.getId().getNamespace(), + "name", shape.getId().getName(), "type", StringUtils.capitalize(shape.getType().toString()), - "members", Writable.map(shape.members(), this::renderMemberIdent), - "memberOpts", Writable.map(shape.members(), this::renderMemberOpt), - "traitOpts", Writable.map(shape.getAllTraits().values(), this::renderTraitOpt, true) + "members", renderMembers(), + "traits", renderTraits(), + "memberVars", Writable.map(shape.members(), this::renderMemberIdent, true) )); } + private Writable renderMembers() { + if (shape.members().isEmpty()) { + return emptyGoTemplate(); + } + return goTemplate(""" + Members: map[string]*smithy.Schema{ + $W + },""", Writable.map(shape.members(), this::renderMemberMapEntry)); + } + + private Writable renderTraits() { + if (shape.getAllTraits().isEmpty()) { + return emptyGoTemplate(); + } + return goTemplate(""" + Traits: map[string]smithy.Trait{ + $W + },""", Writable.map(shape.getAllTraits().values(), this::renderTraitMapEntry)); + } + private Writable renderMemberIdent(MemberShape member) { var memberName = member.getMemberName(); return goTemplate(""" - var $ident:L = $schema:L.Member($name:S) + var $ident:L = $schema:L.Members[$name:S] """, Map.of( "ident", getMemberSchemaName(shape, member), @@ -66,19 +90,30 @@ private Writable renderMemberIdent(MemberShape member) { )); } - private Writable renderMemberOpt(MemberShape member) { + private Writable renderMemberMapEntry(MemberShape member) { var target = ctx.model().expectShape(member.getTarget()); + var memberTraits = member.getAllTraits().values(); - return goTemplate("smithy.WithMember($name:S, $target:L, $traits:W),", + return goTemplate(""" + $name:S: smithy.NewMember($name:S, $target:L$traits:W), + """, Map.of( "name", member.getMemberName(), "target", getSchemaName(target), - "traits", Writable.map(member.getAllTraits().values(), this::renderTraitOpt) + "traits", memberTraits.isEmpty() + ? emptyGoTemplate() + : goTemplate(", $W", Writable.map(memberTraits, this::renderVariadicTrait)) )); } - private Writable renderTraitOpt(Trait trait) { - // FUTURE: load additional generators through GoIntegration + private Writable renderTraitMapEntry(Trait trait) { + var generator = DefaultTraitGenerators.forTrait(trait.toShapeId()); + return generator != null + ? goTemplate("$S: $W,", trait.toShapeId().toString(), generator.render(trait)) + : emptyGoTemplate(); + } + + private Writable renderVariadicTrait(Trait trait) { var generator = DefaultTraitGenerators.forTrait(trait.toShapeId()); return generator != null ? goTemplate("$W,", generator.render(trait)) diff --git a/schema.go b/schema.go index 1f140f3d6..6d92c2d97 100644 --- a/schema.go +++ b/schema.go @@ -53,92 +53,28 @@ func stoid(s string) ShapeID { // Generated clients use schemas at runtime to dynamically (de)serialize // request/responses. type Schema struct { - id ShapeID - typ ShapeType - - members map[string]*Schema // member name -> schema - traits map[string]Trait // trait ID -> trait -} - -// SchemaOptions configures a new Schema. -type SchemaOptions struct { - members []*Schema - traits []Trait + ID ShapeID + Type ShapeType + Members map[string]*Schema // member name -> schema + Traits map[string]Trait // trait ID -> trait } -// WithMember adds a member targeting the given Schema. +// NewMember creates a member schema from a target schema, overriding traits. // -// Traits provided for the member here override any traits on the target if -// there is collision. -func WithMember(name string, target *Schema, traits ...Trait) func(*SchemaOptions) { - return func(o *SchemaOptions) { - m := &Schema{ - id: ShapeID{Member: name}, - typ: target.typ, - members: target.members, - traits: maps.Clone(target.traits), - } - - for _, t := range traits { - m.traits[t.TraitID()] = t - } - - o.members = append(o.members, m) +// Traits provided for the member override any traits on the target if there +// is collision. +func NewMember(name string, target *Schema, traits ...Trait) *Schema { + m := &Schema{ + ID: ShapeID{Member: name}, + Type: target.Type, + Members: target.Members, + Traits: maps.Clone(target.Traits), } -} -// WithTraits adds traits to the Schema. -func WithTraits(traits ...Trait) func(*SchemaOptions) { - return func(o *SchemaOptions) { - o.traits = append(o.traits, traits...) + for _, t := range traits { + m.Traits[t.TraitID()] = t } -} - -// NewSchema returns a schema with the provided members and traits. -// -// Generated clients include schemas for every shape that needs to be -// (de)serialized as part of a service operation in a schemas/ package. -func NewSchema(id string, typ ShapeType, opts ...func(*SchemaOptions)) *Schema { - var o SchemaOptions - for _, opt := range opts { - opt(&o) - } - - sid := stoid(id) - members := make(map[string]*Schema, len(o.members)) - for _, m := range o.members { - m.id.Namespace = sid.Namespace - m.id.Name = sid.Name - members[m.id.Member] = m - } - - traits := make(map[string]Trait, len(o.traits)) - for _, t := range o.traits { - traits[t.TraitID()] = t - } - - return &Schema{ - id: sid, - typ: typ, - members: members, - traits: traits, - } -} - -// ID returns the shape ID for this schema as it appears in the original -// Smithy model. -func (s *Schema) ID() ShapeID { - return s.id -} - -// Type returns the schema's type. -func (s *Schema) Type() ShapeType { - return s.typ -} -// Member returns the named member from the schema. -func (s *Schema) Member(name string) *Schema { - m, _ := s.members[name] return m } @@ -146,7 +82,7 @@ func (s *Schema) Member(name string) *Schema { func SchemaTrait[T Trait](s *Schema) (T, bool) { var trait T - opaque, ok := s.traits[trait.TraitID()] + opaque, ok := s.Traits[trait.TraitID()] if !ok { return trait, false } From d529e950fd83e379de3dfeb661a46f2b11ad1d1e Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Fri, 27 Feb 2026 13:38:53 -0500 Subject: [PATCH 03/38] fixup for event stream updates --- .../smithy/go/codegen/OperationGenerator.java | 4 ++-- .../smithy/go/codegen/StructureGenerator.java | 13 ++++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java index 3d3b194ec..b1e16abab 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java @@ -155,7 +155,7 @@ public void run() { } else { structOutputShape = outputShape; } - new StructureGenerator(ctx, writer, structOutputShape, protocolGenerator) + new StructureGenerator(ctx, writer, structOutputShape, protocolGenerator, symbolProvider.toSymbol(outputShape)) .renderStructure(() -> { if (outputShape.getMemberNames().size() != 0) { writer.write(""); @@ -187,7 +187,7 @@ public void run() { var nonStreamingOutput = outputShape.members().stream().filter(member -> !StreamingTrait.isEventStream(model, member)).toList(); StructureShape initialReplyStruct = outputShape.toBuilder().clearMembers().members(nonStreamingOutput).build(); - new StructureGenerator(model, symbolProvider, writer, service, initialReplyStruct, initialReply, protocolGenerator) + new StructureGenerator(ctx, writer, initialReplyStruct, protocolGenerator, initialReply) .renderStructure(() -> { writer.writeDocs("Metadata pertaining to the operation's result."); writer.write("ResultMetadata $T", metadataSymbol); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java index 97dfedbdb..6f6a2d873 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java @@ -52,7 +52,7 @@ public final class StructureGenerator implements Runnable { private final SymbolProvider symbolProvider; private final GoWriter writer; private final StructureShape shape; - private final Symbol symbol; + private Symbol symbol; private final ServiceShape service; private final ProtocolGenerator protocolGenerator; private final boolean useExperimentalSerde; @@ -77,6 +77,17 @@ public StructureGenerator( this.nilIndex = GoPointableIndex.of(model); } + public StructureGenerator( + GoCodegenContext ctx, + GoWriter writer, + StructureShape shape, + ProtocolGenerator protocolGenerator, + Symbol symbolOverride + ) { + this(ctx, writer, shape, protocolGenerator); + this.symbol = symbolOverride; + } + @Override public void run() { if (!shape.hasTrait(ErrorTrait.class)) { From 44d1bfb0688d01bc517e2ac79cde203a6ef7431b Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Thu, 5 Mar 2026 13:09:22 -0500 Subject: [PATCH 04/38] serde2: implement awsJson10 (#633) * implement awsjson10 minus event streams * forgot about sparse * Update serde.go Co-authored-by: Luis Madrigal <599908+Madrigal@users.noreply.github.com> * Update serde.go Co-authored-by: Luis Madrigal <599908+Madrigal@users.noreply.github.com> * idk what this was about * drop the old todo stuff --------- Co-authored-by: Luis Madrigal <599908+Madrigal@users.noreply.github.com> --- aws-protocols/awsjson10/awsjson10.go | 191 ++++++ aws-protocols/go.mod | 7 + aws-protocols/go.sum | 0 aws-protocols/internal/json/codec.go | 20 + .../internal/json/shape_deserializer.go | 568 ++++++++++++++++++ .../internal/json/shape_serializer.go | 502 ++++++++++++++++ .../go/codegen/AbstractDirectedCodegen.java | 2 +- .../smithy/go/codegen/ClientOptions.java | 20 +- .../smithy/go/codegen/CodegenVisitor.java | 130 ++-- .../smithy/go/codegen/GoCodegenContext.java | 42 ++ .../LegacyJsonProtocolDocumentUtils.java | 49 ++ .../smithy/go/codegen/OperationGenerator.java | 13 +- .../go/codegen/ProtocolDocumentGenerator.java | 61 +- .../smithy/go/codegen/SchemaGenerator.java | 73 +-- .../smithy/go/codegen/ServiceGenerator.java | 15 +- .../smithy/go/codegen/SmithyGoDependency.java | 3 + .../smithy/go/codegen/StructureGenerator.java | 25 +- .../smithy/go/codegen/TypeRegistry.java | 38 ++ .../smithy/go/codegen/UnionGenerator.java | 72 ++- .../go/codegen/serde2/ListDeserializer.java | 154 +++++ .../go/codegen/serde2/ListSerializer.java | 142 +++++ .../go/codegen/serde2/MapDeserializer.java | 156 +++++ .../go/codegen/serde2/MapSerializer.java | 143 +++++ .../Serde2DeserializeResponseMiddleware.java | 57 ++ .../Serde2SerializeRequestMiddleware.java | 61 ++ .../codegen/serde2/StructureDeserializer.java | 127 ++++ .../codegen/serde2/StructureSerializer.java | 99 +++ .../go/codegen/serde2/UnionDeserializer.java | 73 +++ .../go/codegen/serde2/UnionSerializer.java | 62 ++ .../smithy/go/codegen/util/ShapeUtil.java | 5 + document/document.go | 124 +++- middleware/context.go | 17 + schema.go | 96 ++- serde.go | 220 +++++++ transport/http/protocol.go | 19 + type_registry.go | 61 ++ 36 files changed, 3310 insertions(+), 137 deletions(-) create mode 100644 aws-protocols/awsjson10/awsjson10.go create mode 100644 aws-protocols/go.mod create mode 100644 aws-protocols/go.sum create mode 100644 aws-protocols/internal/json/codec.go create mode 100644 aws-protocols/internal/json/shape_deserializer.go create mode 100644 aws-protocols/internal/json/shape_serializer.go create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/LegacyJsonProtocolDocumentUtils.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2DeserializeResponseMiddleware.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2SerializeRequestMiddleware.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionSerializer.java create mode 100644 serde.go create mode 100644 transport/http/protocol.go create mode 100644 type_registry.go diff --git a/aws-protocols/awsjson10/awsjson10.go b/aws-protocols/awsjson10/awsjson10.go new file mode 100644 index 000000000..201fdae9f --- /dev/null +++ b/aws-protocols/awsjson10/awsjson10.go @@ -0,0 +1,191 @@ +package awsjson10 + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + + "github.com/aws/smithy-go" + awsjson "github.com/aws/smithy-go/aws-protocols/internal/json" + smithyio "github.com/aws/smithy-go/io" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// New returns an instance of the awsJson 1.0 protocol. +func New() *Protocol { + return &Protocol{} +} + +// Protocol implements aws.protocols#awsJson10. +type Protocol struct { + UseQueryCompatible bool +} + +var _ smithyhttp.ClientProtocol = (*Protocol)(nil) + +// ID identifies the protocol. +func (*Protocol) ID() string { + return "aws.protocols#awsJson10" +} + +// SerializeRequest serializes a request for AWS Json 1.0. +func (p *Protocol) SerializeRequest( + ctx context.Context, + in smithy.Serializable, + req *smithyhttp.Request, +) error { + req.Method = http.MethodPost + req.Header.Set("X-Amz-Target", fmt.Sprintf("%s.%s", middleware.GetServiceName(ctx), middleware.GetOperationName(ctx))) + req.Header.Set("Content-Type", "application/x-amz-json-1.0") + if p.UseQueryCompatible { + req.Header.Set("X-Amzn-Query-Compatible", "true") + } + + ss := awsjson.NewShapeSerializer() + in.Serialize(ss) + + sreq, err := req.SetStream(bytes.NewReader(ss.Bytes())) + if err != nil { + return fmt.Errorf("set stream: %w", err) + } + + *req = *sreq + return nil +} + +// DeserializeResponse deserializes a response for AWS Json 1.0. +func (p *Protocol) DeserializeResponse( + ctx context.Context, + types *smithy.TypeRegistry, + resp *smithyhttp.Response, + out smithy.Deserializable, +) error { + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return p.deserializeError(types, resp) + } + + payload, err := io.ReadAll(resp.Body) + if err != nil { + return &smithy.DeserializationError{Err: err} + } + + if len(payload) == 0 { + return nil + } + + sd := awsjson.NewShapeDeserializer(payload) + if err := out.Deserialize(sd); err != nil { + return &smithy.DeserializationError{Err: err} + } + + return nil +} + +func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithyhttp.Response) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + var headerCode string + headerCode = response.Header.Get("X-Amzn-ErrorType") + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + bodyInfo, err := getProtocolErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + err = &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + return err + } + + errorBody.Seek(0, io.SeekStart) + if typ, ok := resolveProtocolErrorType(headerCode, bodyInfo); ok { + errorCode = typ + } + if len(bodyInfo.Message) != 0 { + errorMessage = bodyInfo.Message + } + + errorCode = sanitizeErrorCode(errorCode) + + perr, ok := types.DeserializableError(errorCode) + if !ok { + return &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + + } + + errorBody.Seek(0, io.SeekStart) + errorBytes, _ := io.ReadAll(errorBody) + if len(errorBytes) > 0 { + deser := awsjson.NewShapeDeserializer(errorBytes) + if err := perr.Deserialize(deser); err != nil { + return &smithy.DeserializationError{Err: err} + } + } + + return perr +} + +type protocolErrorInfo struct { + Type string `json:"__type"` + Message string + + // nonstandard, but some AWS services do present the type here + Code any +} + +func getProtocolErrorInfo(decoder *json.Decoder) (protocolErrorInfo, error) { + var errInfo protocolErrorInfo + if err := decoder.Decode(&errInfo); err != nil { + if err == io.EOF { + return errInfo, nil + } + return errInfo, err + } + + return errInfo, nil +} + +func resolveProtocolErrorType(headerType string, bodyInfo protocolErrorInfo) (string, bool) { + if len(headerType) != 0 { + return headerType, true + } else if len(bodyInfo.Type) != 0 { + return bodyInfo.Type, true + } else if code, ok := bodyInfo.Code.(string); ok && len(code) != 0 { + return code, true + } + return "", false +} + +// sanitizeErrorCode strips namespace prefixes and URI suffixes from error +// codes received on the wire. +func sanitizeErrorCode(code string) string { + if idx := strings.Index(code, ":"); idx != -1 { + code = code[:idx] + } + if idx := strings.Index(code, "#"); idx != -1 { + code = code[idx+1:] + } + return code +} diff --git a/aws-protocols/go.mod b/aws-protocols/go.mod new file mode 100644 index 000000000..ed1370766 --- /dev/null +++ b/aws-protocols/go.mod @@ -0,0 +1,7 @@ +module github.com/aws/smithy-go/aws-protocols + +go 1.24 + +require github.com/aws/smithy-go v1.24.0 + +replace github.com/aws/smithy-go => ../ diff --git a/aws-protocols/go.sum b/aws-protocols/go.sum new file mode 100644 index 000000000..e69de29bb diff --git a/aws-protocols/internal/json/codec.go b/aws-protocols/internal/json/codec.go new file mode 100644 index 000000000..c139b4ef8 --- /dev/null +++ b/aws-protocols/internal/json/codec.go @@ -0,0 +1,20 @@ +package json + +import ( + "github.com/aws/smithy-go" +) + +// Codec is a JSON codec. +type Codec struct{} + +var _ smithy.Codec = (*Codec)(nil) + +// Serializer returns a JSON shape serializer. +func (c *Codec) Serializer() smithy.ShapeSerializer { + return NewShapeSerializer() +} + +// Deserializer returns a JSON shape deserializer. +func (c *Codec) Deserializer(p []byte) smithy.ShapeDeserializer { + return NewShapeDeserializer(p) +} diff --git a/aws-protocols/internal/json/shape_deserializer.go b/aws-protocols/internal/json/shape_deserializer.go new file mode 100644 index 000000000..4095a336c --- /dev/null +++ b/aws-protocols/internal/json/shape_deserializer.go @@ -0,0 +1,568 @@ +package json + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "math" + "strings" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + smithytime "github.com/aws/smithy-go/time" + "github.com/aws/smithy-go/traits" +) + +// ShapeDeserializer implements unmarshaling of JSON into Smithy shapes. +type ShapeDeserializer struct { + dec *json.Decoder + head stack + + // json.Decoder does not have a Peek() but we need to be able to + // "lookahead" for conditionally pulling a null token out in ReadNil. + peeked json.Token + hasPeek bool +} + +// NewShapeDeserializer creates a new ShapeDeserializer. +func NewShapeDeserializer(p []byte) *ShapeDeserializer { + dec := json.NewDecoder(bytes.NewReader(p)) + dec.UseNumber() + return &ShapeDeserializer{dec: dec} +} + +var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil) + +func (d *ShapeDeserializer) token() (json.Token, error) { + if d.hasPeek { + d.hasPeek = false + return d.peeked, nil + } + return d.dec.Token() +} + +func (d *ShapeDeserializer) more() bool { + if d.hasPeek { + return true + } + return d.dec.More() +} + +func (d *ShapeDeserializer) expectDelim(e json.Delim) error { + tok, err := d.token() + if err != nil { + return err + } + + if a, ok := tok.(json.Delim); ok { + if e != a { + return fmt.Errorf("expect %s, got %s", e, a) + } + return nil + } + + return fmt.Errorf("expect delim, got %T", tok) +} + +// ReadNil implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadNil(s *smithy.Schema) (bool, error) { + tok, err := d.token() + if err != nil { + return false, err + } + if tok == nil { + return true, nil + } + + // The only way to "unread" it is to note it and have token() return it + // next time. + d.peeked = tok + d.hasPeek = true + return false, nil +} + +// ReadInt8 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt8(s *smithy.Schema, v *int8) error { + n, err := d.readInt(math.MinInt8, math.MaxInt8) + *v = int8(n) + return err +} + +// ReadInt16 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt16(s *smithy.Schema, v *int16) error { + n, err := d.readInt(math.MinInt16, math.MaxInt16) + *v = int16(n) + return err +} + +// ReadInt32 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt32(s *smithy.Schema, v *int32) error { + n, err := d.readInt(math.MinInt32, math.MaxInt32) + *v = int32(n) + return err +} + +// ReadInt64 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { + n, err := d.readInt(math.MinInt64, math.MaxInt64) + *v = n + return err +} + +// ReadInt8Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt8Ptr(s *smithy.Schema, v **int8) error { + if *v == nil { + *v = new(int8) + } + return d.ReadInt8(s, *v) +} + +// ReadInt16Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt16Ptr(s *smithy.Schema, v **int16) error { + if *v == nil { + *v = new(int16) + } + return d.ReadInt16(s, *v) +} + +// ReadInt32Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt32Ptr(s *smithy.Schema, v **int32) error { + if *v == nil { + *v = new(int32) + } + return d.ReadInt32(s, *v) +} + +// ReadInt64Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt64Ptr(s *smithy.Schema, v **int64) error { + if *v == nil { + *v = new(int64) + } + return d.ReadInt64(s, *v) +} + +func (d *ShapeDeserializer) readInt(min, max int64) (int64, error) { + tok, err := d.token() + if err != nil { + return 0, err + } + + num, ok := tok.(json.Number) + if !ok { + return 0, fmt.Errorf("expected number, got %T", tok) + } + + n, err := num.Int64() + if err != nil { + return 0, err + } + + if n < min || n > max { + return 0, fmt.Errorf("int %d exceeds range [%d, %d]", n, min, max) + } + + return n, nil +} + +// ReadFloat32 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error { + n, err := d.readFloat() + *v = float32(n) + return err +} + +// ReadFloat64 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { + n, err := d.readFloat() + *v = n + return err +} + +// ReadFloat32Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat32Ptr(s *smithy.Schema, v **float32) error { + if *v == nil { + *v = new(float32) + } + return d.ReadFloat32(s, *v) +} + +// ReadFloat64Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat64Ptr(s *smithy.Schema, v **float64) error { + if *v == nil { + *v = new(float64) + } + return d.ReadFloat64(s, *v) +} + +func (d *ShapeDeserializer) readFloat() (float64, error) { + tok, err := d.token() + if err != nil { + return 0, err + } + + switch v := tok.(type) { + case json.Number: + return v.Float64() + case string: + switch { + case strings.EqualFold(v, "NaN"): + return math.NaN(), nil + case strings.EqualFold(v, "Infinity"): + return math.Inf(1), nil + case strings.EqualFold(v, "-Infinity"): + return math.Inf(-1), nil + default: + return 0, fmt.Errorf("unexpected string value for float: %s", v) + } + default: + return 0, fmt.Errorf("expected number, got %T", tok) + } +} + +// ReadBool implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { + tok, err := d.token() + if err != nil { + return err + } + + b, ok := tok.(bool) + if !ok { + return fmt.Errorf("expected bool, got %T", tok) + } + + *v = b + return nil +} + +// ReadBoolPtr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadBoolPtr(s *smithy.Schema, v **bool) error { + if *v == nil { + *v = new(bool) + } + return d.ReadBool(s, *v) +} + +// ReadString implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { + tok, err := d.token() + if err != nil { + return err + } + + str, ok := tok.(string) + if !ok { + return fmt.Errorf("expected string, got %T", tok) + } + + *v = str + return nil +} + +// ReadStringPtr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadStringPtr(s *smithy.Schema, v **string) error { + if *v == nil { + *v = new(string) + } + return d.ReadString(s, *v) +} + +// ReadTime implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadTime(schema *smithy.Schema, v *time.Time) error { + format := "epoch-seconds" + if t, ok := smithy.SchemaTrait[*traits.TimestampFormat](schema); ok { + format = t.Format + } + + switch format { + case "epoch-seconds": + n, err := d.readFloat() + if err != nil { + return err + } + *v = smithytime.ParseEpochSeconds(n) + return nil + case "date-time": + var s string + if err := d.ReadString(schema, &s); err != nil { + return err + } + t, err := smithytime.ParseDateTime(s) + if err != nil { + return err + } + *v = t + return nil + case "http-date": + var s string + if err := d.ReadString(schema, &s); err != nil { + return err + } + t, err := smithytime.ParseHTTPDate(s) + if err != nil { + return err + } + *v = t + return nil + default: + return fmt.Errorf("unknown timestamp format: %s", format) + } +} + +// ReadTimePtr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadTimePtr(schema *smithy.Schema, v **time.Time) error { + if *v == nil { + *v = new(time.Time) + } + return d.ReadTime(schema, *v) +} + +// ReadBlob implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { + tok, err := d.token() + if err != nil { + return err + } + + str, ok := tok.(string) + if !ok { + return fmt.Errorf("expected string, got %T", tok) + } + + b, err := base64.StdEncoding.DecodeString(str) + if err != nil { + return fmt.Errorf("decode base64 blob: %w", err) + } + + *v = b + return nil +} + +// ReadList implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { + tok, err := d.token() + if err != nil { + return err + } + + delim, ok := tok.(json.Delim) + if !ok || delim != '[' { + return fmt.Errorf("expected '[', got %v", tok) + } + + return nil +} + +// ReadListItem implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadListItem(s *smithy.Schema) (bool, error) { + if !d.more() { + return false, d.expectDelim(']') + } + + return true, nil +} + +// ReadMap implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadMap(s *smithy.Schema) error { + tok, err := d.token() + if err != nil { + return err + } + + delim, ok := tok.(json.Delim) + if !ok || delim != '{' { + return fmt.Errorf("expected '{', got %v", tok) + } + + return nil +} + +// ReadMapKey implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadMapKey(s *smithy.Schema) (string, bool, error) { + if !d.more() { + return "", false, d.expectDelim('}') + } + + tok, err := d.token() + if err != nil { + return "", false, err + } + + key, ok := tok.(string) + if !ok { + return "", false, fmt.Errorf("expected string key, got %T", tok) + } + + return key, true, nil +} + +// ReadStruct implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error { + tok, err := d.token() + if err != nil { + return err + } + + delim, ok := tok.(json.Delim) + if !ok || delim != '{' { + return fmt.Errorf("expected '{', got %v", tok) + } + + d.head.Push(s) + return nil +} + +// ReadStructMember implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { + if !d.more() { + d.head.Pop() + return nil, d.expectDelim('}') + } + + tok, err := d.token() + if err != nil { + return nil, err + } + + key, ok := tok.(string) + if !ok { + return nil, fmt.Errorf("expected string key, got %T", tok) + } + + schema, ok := d.head.Top().(*smithy.Schema) + if !ok { + return nil, fmt.Errorf("ReadStructMember called without ReadStruct?") + } + + member := schema.Member(key) + if member == nil { + // TODO smithy.api#jsonName + if err := d.skip(); err != nil { + return nil, err + } + return d.ReadStructMember() // just try the next one + } + + return member, nil +} + +// ReadUnion implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) { + // Decode the entire union object to find the non-null member. + var raw map[string]json.RawMessage + if err := d.dec.Decode(&raw); err != nil { + return nil, fmt.Errorf("decode union: %w", err) + } + + for key, val := range raw { + if string(val) == "null" { + continue + } + + member := s.Member(key) + if member == nil { + continue + } + + // Replace the decoder with one that reads just this value + dec := json.NewDecoder(bytes.NewReader(val)) + dec.UseNumber() + d.dec = dec + return member, nil + } + + return nil, nil +} + +// used to skip over a struct member that we didn't have a schema for, though +// it also calls itself +func (d *ShapeDeserializer) skip() error { + tok, err := d.token() + if err != nil { + return err + } + + switch v := tok.(type) { + case json.Delim: + switch v { + case '{': + for d.more() { + if _, err := d.token(); err != nil { // the key + return err + } + if err := d.skip(); err != nil { // the value + return err + } + } + _, err := d.token() // the '}' + return err + case '[': + for d.more() { + if err := d.skip(); err != nil { + return err + } + } + _, err := d.token() // the ']' + return err + default: + return fmt.Errorf("unexpected delimiter: %v", v) + } + default: + return nil // scalar, don't have to do anything else + } +} + +// ReadDocument reads a JSON value into a document Value. +// +// For now this produces an [document.Opaque] wrapping the raw decoded value, +// which is what the legacy document bridge in generated code expects. The +// jsonToValue conversion is available for future use when the new typed +// document path is wired up end-to-end. +func (d *ShapeDeserializer) ReadDocument(schema *smithy.Schema, v *document.Value) error { + var raw any + if err := d.dec.Decode(&raw); err != nil { + return err + } + + *v = document.Opaque{Value: raw} + return nil +} + +func jsonToValue(v any) (document.Value, error) { + switch vv := v.(type) { + case nil: + return document.Null{}, nil + case bool: + return document.Boolean(vv), nil + case json.Number: + return document.Number(vv.String()), nil + case float64: + return document.Number(fmt.Sprintf("%v", vv)), nil + case string: + return document.String(vv), nil + case []any: + list := make(document.List, len(vv)) + for i, item := range vv { + dv, err := jsonToValue(item) + if err != nil { + return nil, err + } + list[i] = dv + } + return list, nil + case map[string]any: + m := make(document.Map, len(vv)) + for k, item := range vv { + dv, err := jsonToValue(item) + if err != nil { + return nil, err + } + m[k] = dv + } + return m, nil + default: + return nil, fmt.Errorf("unexpected JSON type %T", v) + } +} diff --git a/aws-protocols/internal/json/shape_serializer.go b/aws-protocols/internal/json/shape_serializer.go new file mode 100644 index 000000000..9ae53f414 --- /dev/null +++ b/aws-protocols/internal/json/shape_serializer.go @@ -0,0 +1,502 @@ +package json + +import ( + "math" + "math/big" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + smithydocumentjson "github.com/aws/smithy-go/document/json" + smithyjson "github.com/aws/smithy-go/encoding/json" + smithytime "github.com/aws/smithy-go/time" + "github.com/aws/smithy-go/traits" +) + +// ShapeSerializer implements marshaling of Smithy shapes to JSON. +type ShapeSerializer struct { + root *smithyjson.Encoder + head stack + + opts ShapeSerializerOptions +} + +// ShapeSerializerOptions configures ShapeSerializer. +type ShapeSerializerOptions struct { + // Controls whether scalar zero values (numbers, strings, bools) are + // written. If false (the default), zero values are not encoded. + // + // Pointer write methods are NOT affected by this option. + WriteZeroValues bool +} + +var _ smithy.ShapeSerializer = (*ShapeSerializer)(nil) + +// NewShapeSerializer creates a new ShapeSerializer. +func NewShapeSerializer(opts ...func(*ShapeSerializerOptions)) *ShapeSerializer { + o := ShapeSerializerOptions{} + for _, fn := range opts { + fn(&o) + } + return &ShapeSerializer{ + root: smithyjson.NewEncoder(), + opts: o, + } +} + +// Bytes returns the serialized JSON bytes. +func (s *ShapeSerializer) Bytes() []byte { + return s.root.Bytes() +} + +// WriteInt8Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8Ptr(schema *smithy.Schema, v *int8) { + if v != nil { + s.withWriteZero(func() { s.WriteInt8(schema, *v) }) + } +} + +// WriteInt16Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16Ptr(schema *smithy.Schema, v *int16) { + if v != nil { + s.withWriteZero(func() { s.WriteInt16(schema, *v) }) + } +} + +// WriteInt32Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32Ptr(schema *smithy.Schema, v *int32) { + if v != nil { + s.withWriteZero(func() { s.WriteInt32(schema, *v) }) + } +} + +// WriteInt64Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64Ptr(schema *smithy.Schema, v *int64) { + if v != nil { + s.withWriteZero(func() { s.WriteInt64(schema, *v) }) + } +} + +// WriteFloat32Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32Ptr(schema *smithy.Schema, v *float32) { + if v != nil { + s.withWriteZero(func() { s.WriteFloat32(schema, *v) }) + } +} + +// WriteFloat64Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64Ptr(schema *smithy.Schema, v *float64) { + if v != nil { + s.withWriteZero(func() { s.WriteFloat64(schema, *v) }) + } +} + +// WriteBoolPtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBoolPtr(schema *smithy.Schema, v *bool) { + if v != nil { + s.withWriteZero(func() { s.WriteBool(schema, *v) }) + } +} + +// WriteStringPtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStringPtr(schema *smithy.Schema, v *string) { + if v != nil { + s.withWriteZero(func() { s.WriteString(schema, *v) }) + } +} + +func (s *ShapeSerializer) withWriteZero(fn func()) { + prev := s.opts.WriteZeroValues + s.opts.WriteZeroValues = true + fn() + s.opts.WriteZeroValues = prev +} + +// WriteBool implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { + if !s.opts.WriteZeroValues && !v { + return + } + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + enc.Key(schema.MemberName()).Boolean(v) + case *smithyjson.Array: + enc.Value().Boolean(v) + default: + s.root.Boolean(v) + } +} + +// WriteInt8 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { + if !s.opts.WriteZeroValues && v == 0 { + return + } + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + enc.Key(schema.MemberName()).Byte(v) + case *smithyjson.Array: + enc.Value().Byte(v) + default: + s.root.Byte(v) + } +} + +// WriteInt16 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { + if !s.opts.WriteZeroValues && v == 0 { + return + } + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + enc.Key(schema.MemberName()).Short(v) + case *smithyjson.Array: + enc.Value().Short(v) + default: + s.root.Short(v) + } +} + +// WriteInt32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { + if !s.opts.WriteZeroValues && v == 0 { + return + } + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + enc.Key(schema.MemberName()).Integer(v) + case *smithyjson.Array: + enc.Value().Integer(v) + case smithyjson.Value: + enc.Integer(v) + s.head.Pop() + default: + s.root.Integer(v) + } +} + +// WriteInt64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { + if !s.opts.WriteZeroValues && v == 0 { + return + } + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + enc.Key(schema.MemberName()).Long(v) + case *smithyjson.Array: + enc.Value().Long(v) + default: + s.root.Long(v) + } +} + +// WriteFloat32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { + if !s.opts.WriteZeroValues && v == 0 { + return + } + + var jv smithyjson.Value + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + jv = enc.Key(schema.MemberName()) + case *smithyjson.Array: + jv = enc.Value() + default: + s.root.Float(v) + return + } + + if math.IsInf(float64(v), 1) { + jv.String("Infinity") + } else if math.IsInf(float64(v), -1) { + jv.String("-Infinity") + } else if math.IsNaN(float64(v)) { + jv.String("NaN") + } +} + +// WriteFloat64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { + if !s.opts.WriteZeroValues && v == 0 { + return + } + + var jv smithyjson.Value + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + jv = enc.Key(schema.MemberName()) + case *smithyjson.Array: + jv = enc.Value() + default: + s.root.Double(v) + return + } + + if math.IsInf(v, 1) { + jv.String("Infinity") + } else if math.IsInf(v, -1) { + jv.String("-Infinity") + } else if math.IsNaN(v) { + jv.String("NaN") + } +} + +// WriteString implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { + if !s.opts.WriteZeroValues && v == "" { + return + } + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + enc.Key(schema.MemberName()).String(v) + case *smithyjson.Array: + enc.Value().String(v) + case smithyjson.Value: + enc.String(v) + s.head.Pop() + default: + s.root.Value.String(v) + } +} + +// WriteBlob implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + enc.Key(schema.MemberName()).Base64EncodeBytes(v) + case *smithyjson.Array: + enc.Value().Base64EncodeBytes(v) + case smithyjson.Value: + enc.Base64EncodeBytes(v) + s.head.Pop() + default: + s.root.Value.Base64EncodeBytes(v) + } +} + +// WriteList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + s.head.Push(enc.Key(schema.MemberName()).Array()) + case *smithyjson.Array: + s.head.Push(enc.Value().Array()) + case smithyjson.Value: + s.head.Push(enc.Array()) + default: + s.head.Push(s.root.Array()) + } +} + +// CloseList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseList() { + if enc, ok := s.head.Top().(*smithyjson.Array); ok { + enc.Close() + s.head.Pop() + } +} + +// WriteMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + s.head.Push(enc.Key(schema.MemberName()).Object()) + case *smithyjson.Array: + s.head.Push(enc.Value().Object()) + case smithyjson.Value: + s.head.Push(enc.Object()) + default: + s.head.Push(s.root.Object()) + } +} + +// WriteKey implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteKey(schema *smithy.Schema, key string) { + if enc, ok := s.head.Top().(*smithyjson.Object); ok { + s.head.Push(enc.Key(key)) + } +} + +// CloseMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseMap() { + if enc, ok := s.head.Top().(*smithyjson.Object); ok { + enc.Close() + s.head.Pop() + + // if this is a map _inside_ a map, pop off the underlying key encoder + // as well (for scalar values that's not necessarily since we can + // deterministically do it there) + if _, ok := s.head.Top().(smithyjson.Value); ok { + s.head.Pop() + } + } +} + +// WriteTime implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { + format := "epoch-seconds" + if t, ok := smithy.SchemaTrait[*traits.TimestampFormat](schema); ok { + format = t.Format + } + + switch format { + case "date-time": + s.WriteString(schema, smithytime.FormatDateTime(v)) + case "http-date": + s.WriteString(schema, smithytime.FormatHTTPDate(v)) + default: + s.WriteFloat64(schema, smithytime.FormatEpochSeconds(v)) + } +} + +// WriteTimePtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { + if v != nil { + s.WriteTime(schema, *v) + } +} + +// WriteUnion implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema, v smithy.Serializable) { + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + s.head.Push(enc.Key(schema.MemberName()).Object()) + case *smithyjson.Array: + s.head.Push(enc.Value().Object()) + case smithyjson.Value: + s.head.Push(enc.Object()) + default: + s.head.Push(s.root.Object()) + } + + top := s.head.Top().(*smithyjson.Object) + s.head.Push(top.Key(variant.MemberName())) + + v.Serialize(s) + + top.Close() + s.head.Pop() +} + +// WriteStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema, v smithy.Serializable) { + if v == nil { + return + } + + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + s.head.Push(enc.Key(schema.MemberName())) + case *smithyjson.Array: + s.head.Push(enc.Value()) + } + + v.Serialize(s) +} + +// WriteNil implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteNil(schema *smithy.Schema) { + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + enc.Key(schema.MemberName()).Null() + case *smithyjson.Array: + enc.Value().Null() + case smithyjson.Value: + enc.Null() + s.head.Pop() + default: + s.root.Null() + } +} + +// WriteBigInteger is unimplemented and will panic. +func (s *ShapeSerializer) WriteBigInteger(schema *smithy.Schema, v big.Int) { + panic("unimplemented") +} + +// WriteBigDecimal is unimplemented and will panic. +func (s *ShapeSerializer) WriteBigDecimal(schema *smithy.Schema, v big.Float) { + panic("unimplemented") +} + +// WriteDocument writes a document value to JSON. +func (s *ShapeSerializer) WriteDocument(schema *smithy.Schema, v document.Value) { + switch vv := v.(type) { + case document.Null: + s.WriteNil(schema) + case document.Boolean: + s.WriteBool(schema, bool(vv)) + case document.Number: + s.writeDocumentRaw(schema, []byte(vv)) + case document.String: + s.WriteString(schema, string(vv)) + case document.Blob: + s.WriteBlob(schema, []byte(vv)) + case document.Timestamp: + s.WriteTime(schema, time.Time(vv)) + case document.List: + s.WriteList(schema) + for _, item := range vv { + s.WriteDocument(schema.ListMember(), item) + } + s.CloseList() + case document.Map: + s.WriteMap(schema) + for k, item := range vv { + s.WriteKey(schema.MapKey(), k) + s.WriteDocument(schema.MapValue(), item) + } + s.CloseMap() + case document.Structure: + s.WriteMap(schema) + for k, item := range vv.Members { + s.WriteKey(nil, k) + s.WriteDocument(nil, item) + } + s.CloseMap() + case document.Opaque: + denc := smithydocumentjson.NewEncoder() + p, _ := denc.Encode(vv.Value) + s.writeDocumentRaw(schema, p) + } +} + +func (s *ShapeSerializer) writeDocumentRaw(schema *smithy.Schema, p []byte) { + switch enc := s.head.Top().(type) { + case *smithyjson.Object: + enc.Key(schema.MemberName()).Write(p) + case *smithyjson.Array: + enc.Value().Write(p) + case smithyjson.Value: + enc.Write(p) + s.head.Pop() + default: + s.root.Write(p) + } +} + +type stack struct { + values []any +} + +type empty struct{} + +func (s *stack) Top() any { + if len(s.values) == 0 { + return empty{} + } + return s.values[len(s.values)-1] +} + +func (s *stack) Push(v any) { + s.values = append(s.values, v) +} + +func (s *stack) Pop() { + s.values = s.values[:len(s.values)-1] +} + +func (s *stack) Len() int { + return len(s.values) +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/AbstractDirectedCodegen.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/AbstractDirectedCodegen.java index 8043d8bb5..820744b4a 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/AbstractDirectedCodegen.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/AbstractDirectedCodegen.java @@ -114,7 +114,7 @@ public void generateError(GenerateErrorDirective d public void generateUnion(GenerateUnionDirective directive) { var delegator = directive.context().writerDelegator(); delegator.useShapeWriter(directive.shape(), writer -> - new UnionGenerator(directive.model(), directive.symbolProvider(), directive.shape()) + new UnionGenerator(directive.context(), directive.model(), directive.symbolProvider(), directive.shape()) .generateUnion(writer) ); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ClientOptions.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ClientOptions.java index 864bdfb37..3dc963102 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ClientOptions.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ClientOptions.java @@ -15,6 +15,7 @@ package software.amazon.smithy.go.codegen; +import static software.amazon.smithy.go.codegen.GoWriter.emptyGoTemplate; import static software.amazon.smithy.go.codegen.GoWriter.goDocTemplate; import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; @@ -40,13 +41,15 @@ public class ClientOptions implements Writable { public static final String NAME = "Options"; + private final GoCodegenContext ctx; private final ProtocolGenerator.GenerationContext context; private final ApplicationProtocol protocol; private final List fields; private final Map authSchemes; - public ClientOptions(ProtocolGenerator.GenerationContext context, ApplicationProtocol protocol) { + public ClientOptions(GoCodegenContext ctx, ProtocolGenerator.GenerationContext context, ApplicationProtocol protocol) { + this.ctx = ctx; this.context = context; this.protocol = protocol; @@ -82,6 +85,8 @@ private Writable generate() { $fields:W + $experimentalSerdeProtocolFields:W + $protocolFields:W } @@ -100,10 +105,21 @@ private Writable generate() { "protocolFields", generateProtocolFields(), "copy", generateCopy(), "getIdentityResolver", generateGetIdentityResolver(), - "helpers", generateHelpers() + "helpers", generateHelpers(), + "experimentalSerdeProtocolFields", ctx.settings().useExperimentalSerde() + ? generateExperimentalSerdeProtocolFields() + : emptyGoTemplate() )); } + private Writable generateExperimentalSerdeProtocolFields() { + ensureSupportedProtocol(); + return goTemplate(""" + $D + Protocol smithyhttp.ClientProtocol + """, SmithyGoDependency.SMITHY_HTTP_TRANSPORT); + } + private Writable generateProtocolTypes() { ensureSupportedProtocol(); return goTemplate(""" diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java index ef8b73d07..ad1a484d4 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java @@ -15,13 +15,10 @@ package software.amazon.smithy.go.codegen; -import static java.util.stream.Collectors.toSet; - import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -38,22 +35,31 @@ import software.amazon.smithy.go.codegen.integration.GoIntegration; import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin; +import software.amazon.smithy.go.codegen.serde2.ListDeserializer; +import software.amazon.smithy.go.codegen.serde2.ListSerializer; +import software.amazon.smithy.go.codegen.serde2.MapDeserializer; +import software.amazon.smithy.go.codegen.serde2.MapSerializer; +import software.amazon.smithy.go.codegen.serde2.Serde2DeserializeResponseMiddleware; +import software.amazon.smithy.go.codegen.serde2.Serde2SerializeRequestMiddleware; +import software.amazon.smithy.go.codegen.serde2.UnionDeserializer; +import software.amazon.smithy.go.codegen.serde2.UnionSerializer; +import software.amazon.smithy.go.codegen.util.ShapeUtil; import software.amazon.smithy.model.Model; import software.amazon.smithy.model.knowledge.ServiceIndex; import software.amazon.smithy.model.knowledge.TopDownIndex; import software.amazon.smithy.model.neighbor.Walker; import software.amazon.smithy.model.shapes.IntEnumShape; +import software.amazon.smithy.model.shapes.ListShape; +import software.amazon.smithy.model.shapes.MapShape; import software.amazon.smithy.model.shapes.OperationShape; import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.ShapeId; -import software.amazon.smithy.model.shapes.ShapeType; import software.amazon.smithy.model.shapes.ShapeVisitor; import software.amazon.smithy.model.shapes.StringShape; import software.amazon.smithy.model.shapes.StructureShape; import software.amazon.smithy.model.shapes.UnionShape; import software.amazon.smithy.model.traits.EnumTrait; -import software.amazon.smithy.model.traits.ErrorTrait; import software.amazon.smithy.model.transform.ModelTransformer; import software.amazon.smithy.utils.OptionalUtils; import software.amazon.smithy.utils.SmithyInternalApi; @@ -223,6 +229,10 @@ void execute() { TopDownIndex.of(model).getContainedOperations(service) .forEach(eventStreamGenerator::generateOperationEventStreamStructure); + // All of these things will completely go away when serde2 is done. + // + // There is a block further down after the serde2 stuff that also uses the protocol generator, but that all will + // have to be pulled out of ProtocolGenerator and just be generic. if (!settings.useExperimentalSerde() && protocolGenerator != null) { LOGGER.info("Generating serde for protocol " + protocolGenerator.getProtocol() + " on " + service.getId()); ProtocolGenerator.GenerationContext.Builder contextBuilder = ProtocolGenerator.GenerationContext.builder() @@ -255,43 +265,73 @@ void execute() { }); } - LOGGER.info("Generating protocol " + protocolGenerator.getProtocol() - + " unit tests for " + service.getId()); - writers.useFileWriter("protocol_test.go", settings.getModuleName(), writer -> { - protocolGenerator.generateProtocolTests(contextBuilder.writer(writer).build()); - }); - protocolDocumentGenerator.generateInternalDocumentTypes(protocolGenerator, contextBuilder.build()); } + LOGGER.info("Generating protocol tests for " + service.getId()); + ProtocolUtils.generateHttpProtocolTests(ctx); + if (settings.useExperimentalSerde()) { - var walker = new Walker(model); - var operations = TopDownIndex.of(model).getContainedOperations(service); - - var shapes = new HashSet(); - shapes.addAll(operations.stream() - .map(it -> model.expectShape(it.getInputShape())) - .flatMap(it -> walker.walkShapes(it).stream()) - .collect(toSet())); - shapes.addAll(operations.stream() - .map(it -> model.expectShape(it.getOutputShape())) - .flatMap(it -> walker.walkShapes(it).stream()) - .collect(toSet())); - shapes.addAll(model.getStructureShapesWithTrait(ErrorTrait.class).stream() - .flatMap(it -> walker.walkShapes(it).stream()) - .collect(toSet())); - - var sortedShapes = new ArrayList<>(shapes.stream() - .sorted() - .filter(it -> it.getType() != ShapeType.MEMBER) - .toList()); - - sortedShapes.add(StructureShape.builder().id("smithy.api#Unit").build()); - - ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", - Writable.map(sortedShapes, it -> new SchemaGenerator(ctx, it), true)); + protocolDocumentGenerator.generateLegacyInternalDocumentTypes(ctx); + + var shapes = new ArrayList<>(ctx.serdeShapes()); + shapes.add(ShapeUtil.UNIT); // targeted by enum members, just generate a blank schema for it + + ctx.writerDelegator().useFileWriter("type_registry.go", settings.getModuleName(), new TypeRegistry(ctx)); + + ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", writer -> { + for (Shape shape : shapes) { + new SchemaGenerator(ctx, shape).accept(writer); + } + + writer.write(""); + writer.writeDocs("Initialize schema members after all schemas are declared to avoid initialization cycles"); + writer.openBlock("func init() {", "}", () -> { + for (Shape shape : shapes) { + new SchemaGenerator(ctx, shape).acceptMembersInit(writer); + } + }); + }); + + var lists = ctx.serdeShapes(ListShape.class); + var maps = ctx.serdeShapes(MapShape.class); + var unionSerdes = ctx.serdeShapes(UnionShape.class); + + // unfortunately since we have input/output in the top-level package and nested shapes in types/ we have to + // generate these twice since we don't want to export them + // + // also rn we don't check for what's actually used in either package at all but DCE should take care of that + + ctx.writerDelegator().useFileWriter("common_serde.go", settings.getModuleName(), + Writable.map(unionSerdes, it -> new UnionSerializer(ctx, it), true)); + ctx.writerDelegator().useFileWriter("types/common_serde.go", settings.getModuleName() + "/types", + Writable.map(unionSerdes, it -> new UnionSerializer(ctx, it), true)); + ctx.writerDelegator().useFileWriter("common_serde.go", settings.getModuleName(), + Writable.map(unionSerdes, it -> new UnionDeserializer(ctx, it), true)); + ctx.writerDelegator().useFileWriter("types/common_serde.go", settings.getModuleName() + "/types", + Writable.map(unionSerdes, it -> new UnionDeserializer(ctx, it), true)); + + ctx.writerDelegator().useFileWriter("common_serde.go", settings.getModuleName(), + Writable.map(lists, it -> new ListSerializer(ctx, it), true)); + ctx.writerDelegator().useFileWriter("types/common_serde.go", settings.getModuleName() + "/types", + Writable.map(lists, it -> new ListSerializer(ctx, it), true)); + ctx.writerDelegator().useFileWriter("common_serde.go", settings.getModuleName(), + Writable.map(lists, it -> new ListDeserializer(ctx, it), true)); + ctx.writerDelegator().useFileWriter("types/common_serde.go", settings.getModuleName() + "/types", + Writable.map(lists, it -> new ListDeserializer(ctx, it), true)); + + ctx.writerDelegator().useFileWriter("common_serde.go", settings.getModuleName(), + Writable.map(maps, it -> new MapSerializer(ctx, it), true)); + ctx.writerDelegator().useFileWriter("types/common_serde.go", settings.getModuleName() + "/types", + Writable.map(maps, it -> new MapSerializer(ctx, it), true)); + ctx.writerDelegator().useFileWriter("common_serde.go", settings.getModuleName(), + Writable.map(maps, it -> new MapDeserializer(ctx, it), true)); + ctx.writerDelegator().useFileWriter("types/common_serde.go", settings.getModuleName() + "/types", + Writable.map(maps, it -> new MapDeserializer(ctx, it), true)); } + // This is all stuff that we'll still need to do but it DEPENDS on ProtocolGenerator right now + // // TODO: With serde/schema decoupling, protocol generators are going away. Endpoint/auth resolution is going to // need to be decoupled from that and I don't really know how yet. Probably just separate integrations / // integration hooks. @@ -318,11 +358,6 @@ void execute() { ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); protocolGenerator.generateEndpointResolutionTests(context); }); - - LOGGER.info("Generating protocol tests for " + service.getId()); - ProtocolUtils.generateHttpProtocolTests(ctx); - - protocolDocumentGenerator.generateInternalDocumentTypes(protocolGenerator, contextBuilder.build()); } LOGGER.fine("Flushing go writers"); @@ -366,9 +401,13 @@ public Void stringShape(StringShape shape) { @Override public Void unionShape(UnionShape shape) { - UnionGenerator generator = new UnionGenerator(model, symbolProvider, shape); + UnionGenerator generator = new UnionGenerator(ctx, model, symbolProvider, shape); writers.useShapeWriter(shape, generator::generateUnion); writers.useShapeExportedTestWriter(shape, generator::generateUnionExamples); + + if (settings.useExperimentalSerde()) { + // TODO schema probably + } return null; } @@ -419,7 +458,12 @@ public Void serviceShape(ServiceShape shape) { } }); - var clientOptions = new ClientOptions(context, protocol); + if (ctx.settings().useExperimentalSerde()) { + writers.useShapeWriter(shape, new Serde2SerializeRequestMiddleware()); + writers.useShapeWriter(shape, new Serde2DeserializeResponseMiddleware()); + } + + var clientOptions = new ClientOptions(ctx, context, protocol); writers.useFileWriter("options.go", settings.getModuleName(), clientOptions); return null; } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoCodegenContext.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoCodegenContext.java index 6f4a197e1..b842abf54 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoCodegenContext.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoCodegenContext.java @@ -15,6 +15,9 @@ package software.amazon.smithy.go.codegen; +import static java.util.stream.Collectors.toSet; + +import java.util.HashSet; import java.util.List; import software.amazon.smithy.build.FileManifest; import software.amazon.smithy.codegen.core.CodegenContext; @@ -22,7 +25,12 @@ import software.amazon.smithy.codegen.core.WriterDelegator; import software.amazon.smithy.go.codegen.integration.GoIntegration; import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.knowledge.TopDownIndex; +import software.amazon.smithy.model.neighbor.Walker; import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeType; +import software.amazon.smithy.model.traits.ErrorTrait; import software.amazon.smithy.utils.SmithyInternalApi; @SmithyInternalApi @@ -37,4 +45,38 @@ public record GoCodegenContext( public ServiceShape service() { return settings.getService(model); } + + /** + * Return all **non-member** shapes in the (de)serialization tree of the service. That is, every shape in the tree + * of the input/outputs of all operations, plus all structures with @error. + */ + public List serdeShapes() { + var walker = new Walker(model); + var operations = TopDownIndex.of(model).getContainedOperations(service()); + + var shapes = new HashSet(); + shapes.addAll(operations.stream() + .map(it -> model.expectShape(it.getInputShape())) + .flatMap(it -> walker.walkShapes(it).stream()) + .collect(toSet())); + shapes.addAll(operations.stream() + .map(it -> model.expectShape(it.getOutputShape())) + .flatMap(it -> walker.walkShapes(it).stream()) + .collect(toSet())); + shapes.addAll(model.getStructureShapesWithTrait(ErrorTrait.class).stream() + .flatMap(it -> walker.walkShapes(it).stream()) + .collect(toSet())); + + return shapes.stream() + .sorted() + .filter(it -> it.getType() != ShapeType.MEMBER) + .toList(); + } + + public List serdeShapes(Class clazz) { + return serdeShapes().stream() + .filter(clazz::isInstance) + .map(clazz::cast) + .toList(); + } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/LegacyJsonProtocolDocumentUtils.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/LegacyJsonProtocolDocumentUtils.java new file mode 100644 index 000000000..fffd44cbc --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/LegacyJsonProtocolDocumentUtils.java @@ -0,0 +1,49 @@ +package software.amazon.smithy.go.codegen; + +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; + +// This is copied almost entirely as-is from JsonProtocolDocumentUtils. It is used for schema-serde to preserve the +// legacy APIs and behavior of documents, which at the time of migration, only supported JSON. Clients using +// schema-based serde don't use these, instead they use Value() and UnmarshalDocument() for requests and responses. +public final class LegacyJsonProtocolDocumentUtils { + public static void generateProtocolDocumentMarshalerUnmarshalDocument(GoCodegenContext context, GoWriter writer) { + writer.write("mBytes, err := m.$L()", ProtocolDocumentGenerator.MARSHAL_SMITHY_DOCUMENT_METHOD); + writer.write("if err != nil { return err }").write(""); + writer.write("jDecoder := $T($T(mBytes))", SymbolUtils.createValueSymbolBuilder("NewDecoder", + SmithyGoDependency.JSON).build(), SymbolUtils.createValueSymbolBuilder("NewReader", + SmithyGoDependency.BYTES).build()); + writer.write("jDecoder.UseNumber()").write(""); + + writer.write("var jv interface{}"); + writer.openBlock("if err := jDecoder.Decode(&v); err != nil {", "}", () -> writer.write("return err")) + .write(""); + + Symbol newUnmarshaler = ProtocolDocumentGenerator.Utilities.getInternalDocumentSymbolBuilder( + context.settings(), ProtocolDocumentGenerator.INTERNAL_NEW_DOCUMENT_UNMARSHALER_FUNC) + .build(); + + writer.write("return $T(v).$L(&jv)", newUnmarshaler, + ProtocolDocumentGenerator.UNMARSHAL_SMITHY_DOCUMENT_METHOD); + } + + public static void generateProtocolDocumentMarshalerMarshalDocument(GoCodegenContext context, GoWriter writer) { + Symbol newEncoder = SymbolUtils.createValueSymbolBuilder("NewEncoder", SmithyGoDependency.SMITHY_DOCUMENT_JSON) + .build(); + + writer.write("return $T().Encode(m.value)", newEncoder); + } + + public static void generateProtocolDocumentUnmarshalerUnmarshalDocument(GoCodegenContext context, GoWriter writer) { + Symbol newDecoder = SymbolUtils.createValueSymbolBuilder("NewDecoder", SmithyGoDependency.SMITHY_DOCUMENT_JSON) + .build(); + + writer.write("decoder := $T()", newDecoder); + writer.write("return decoder.DecodeJSONInterface(m.value, v)"); + } + + public static void generateProtocolDocumentUnmarshalerMarshalDocument(GoCodegenContext context, GoWriter writer) { + writer.write("return $T(m.value)", + SymbolUtils.createValueSymbolBuilder("Marshal", SmithyGoDependency.JSON).build()); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java index b1e16abab..0f90f2940 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java @@ -54,6 +54,8 @@ public final class OperationGenerator implements Runnable { private final Symbol operationSymbol; private final ProtocolGenerator protocolGenerator; private final List runtimeClientPlugins; + private final StructureShape input; + private final StructureShape output; OperationGenerator( GoCodegenContext ctx, @@ -71,6 +73,8 @@ public final class OperationGenerator implements Runnable { this.operationSymbol = ctx.symbolProvider().toSymbol(operation); this.protocolGenerator = protocolGenerator; this.runtimeClientPlugins = runtimeClientPlugins; + this.input = ctx.model().expectShape(operation.getInputShape(), StructureShape.class); + this.output = ctx.model().expectShape(operation.getOutputShape(), StructureShape.class); } @Override @@ -287,7 +291,14 @@ private void generateOperationProtocolMiddlewareAdders() { writer.write("err = stack.Deserialize.Add(&$L{}, middleware.After)", deserializerMiddlewareName); writer.write("if err != nil { return err }"); } else { - // TODO + writer.write(""" + if err := stack.Serialize.Add(&serializeRequestMiddleware{options: &options}, middleware.After); err != nil { + return err + }"""); + writer.write(""" + if err := stack.Deserialize.Add(&deserializeResponseMiddleware{options: &options, output: &$T{}}, middleware.After); err != nil { + return err + }""", symbolProvider.toSymbol(output)); } // FUTURE: retry middleware should be at the front of finalize, right now it's added by the SDK diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java index d14e180af..8074847c2 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java @@ -353,6 +353,62 @@ public void generateInternalDocumentTypes(ProtocolGenerator protocolGenerator, G }); } + // Used for schema-serde, generates legacy JSON-based document support to preserve existing APIs/behavior. + // Copied from above generateInternalDocumentTypes which will eventually be deleted when schema-serde is complete. + public void generateLegacyInternalDocumentTypes(GoCodegenContext ctx) { + if (!this.hasDocumentShapes) { + return; + } + + writeInternalDocumentPackage("document.go", writer -> { + Symbol marshalerSymbol = getInternalDocumentSymbol("documentMarshaler", + true); + Symbol unmarshalerSymbol = getInternalDocumentSymbol("documentUnmarshaler", true); + + Symbol isDocumentInterface = getInternalDocumentSymbol(INTERNAL_IS_DOCUMENT_INTERFACE); + + writeInternalDocumentImplementation( + writer, + marshalerSymbol, + () -> LegacyJsonProtocolDocumentUtils.generateProtocolDocumentMarshalerUnmarshalDocument(ctx, writer), + () -> LegacyJsonProtocolDocumentUtils.generateProtocolDocumentMarshalerMarshalDocument(ctx, writer)); + writeInternalDocumentImplementation(writer, + unmarshalerSymbol, + () -> LegacyJsonProtocolDocumentUtils.generateProtocolDocumentUnmarshalerUnmarshalDocument(ctx, writer), + () -> LegacyJsonProtocolDocumentUtils.generateProtocolDocumentUnmarshalerMarshalDocument(ctx, writer)); + + Symbol documentInterfaceSymbol = getInternalDocumentSymbol(DOCUMENT_INTERFACE_NAME); + + writer.writeDocs(String.format("%s creates a new document marshaler for the given input type", + INTERNAL_NEW_DOCUMENT_MARSHALER_FUNC)); + writer.openBlock("func $L(v interface{}) $T {", "}", INTERNAL_NEW_DOCUMENT_MARSHALER_FUNC, + documentInterfaceSymbol, () -> { + writer.openBlock("return &$T{", "}", marshalerSymbol, () -> { + writer.write("value: v,"); + }); + }).write(""); + + writer.writeDocs(String.format("%s creates a new document unmarshaler for the given service response", + INTERNAL_NEW_DOCUMENT_UNMARSHALER_FUNC)); + writer.openBlock("func $L(v interface{}) $T {", "}", INTERNAL_NEW_DOCUMENT_UNMARSHALER_FUNC, + documentInterfaceSymbol, () -> { + writer.openBlock("return &$T{", "}", unmarshalerSymbol, () -> { + writer.write("value: v,"); + }); + }).write(""); + + writer.writeDocs(String.format("%s returns whether the given Interface implementation is" + + " a valid client implementation", isDocumentInterface)); + writer.openBlock("func $T(v Interface) (ok bool) {", "}", isDocumentInterface, () -> { + writer.openBlock("defer func() {", "}()", () -> { + writer.openBlock("if err := recover(); err != nil {", "}", () -> writer.write("ok = false")); + }); + writer.write("v.$L()", IS_SMITHY_DOCUMENT_METHOD); + writer.write("return true"); + }).write(""); + }); + } + private void writeInternalDocumentImplementation( GoWriter writer, Symbol typeSymbol, @@ -364,10 +420,11 @@ private void writeInternalDocumentImplementation( }); writer.write(""); + // for schema-serde (if the service doesn't do that yet then this is just unused for now) + writer.write("func (m $P) Value() any { return m.value }", typeSymbol); + writer.openBlock("func (m $P) $L(v interface{}) error {", "}", typeSymbol, UNMARSHAL_SMITHY_DOCUMENT_METHOD, unmarshalMethodDefinition); - writer.write(""); - writer.openBlock("func (m $P) $L() ([]byte, error) {", "}", typeSymbol, MARSHAL_SMITHY_DOCUMENT_METHOD, marshalMethodDefinition); writer.write(""); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java index 6883b6220..6878cf20d 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java @@ -36,76 +36,65 @@ public static String getMemberSchemaName(Shape shape, MemberShape member) { public void accept(GoWriter writer) { writer.addUseImports(SmithyGoDependency.SMITHY); writer.writeGoTemplate(""" - var $ident:L = &smithy.Schema{ - ID: smithy.ShapeID{ - Namespace: $namespace:S, - Name: $name:S, - }, - Type: smithy.ShapeType$type:L, - $members:W - $traits:W - } - $memberVars:W + var $ident:L = smithy.NewSchema(smithy.ShapeID{ + Namespace: $namespace:S, + Name: $name:S, + }, smithy.ShapeType$type:L, $numMembers:L$traits:W) + $memberVarDecls:W """, Map.of( "ident", getSchemaName(shape), "namespace", shape.getId().getNamespace(), "name", shape.getId().getName(), "type", StringUtils.capitalize(shape.getType().toString()), - "members", renderMembers(), - "traits", renderTraits(), - "memberVars", Writable.map(shape.members(), this::renderMemberIdent, true) + "numMembers", shape.members().size(), + "traits", renderVariadicTraits(shape.getAllTraits().values()), + "memberVarDecls", Writable.map(shape.members(), this::renderMemberVarDecl, true) )); } - private Writable renderMembers() { + public void acceptMembersInit(GoWriter writer) { if (shape.members().isEmpty()) { - return emptyGoTemplate(); - } - return goTemplate(""" - Members: map[string]*smithy.Schema{ - $W - },""", Writable.map(shape.members(), this::renderMemberMapEntry)); - } - - private Writable renderTraits() { - if (shape.getAllTraits().isEmpty()) { - return emptyGoTemplate(); + return; } - return goTemplate(""" - Traits: map[string]smithy.Trait{ - $W - },""", Writable.map(shape.getAllTraits().values(), this::renderTraitMapEntry)); + writer.writeGoTemplate(""" + $memberAddAndAssign:W + """, + Map.of( + "memberAddAndAssign", Writable.map(shape.members(), this::renderMemberAddAndAssign, true) + )); } - private Writable renderMemberIdent(MemberShape member) { - var memberName = member.getMemberName(); + private Writable renderMemberVarDecl(MemberShape member) { return goTemplate(""" - var $ident:L = $schema:L.Members[$name:S] + var $ident:L *smithy.Schema """, - Map.of( - "ident", getMemberSchemaName(shape, member), - "schema", getSchemaName(shape), - "name", memberName - )); + Map.of("ident", getMemberSchemaName(shape, member))); } - private Writable renderMemberMapEntry(MemberShape member) { + private Writable renderMemberAddAndAssign(MemberShape member) { var target = ctx.model().expectShape(member.getTarget()); var memberTraits = member.getAllTraits().values(); return goTemplate(""" - $name:S: smithy.NewMember($name:S, $target:L$traits:W), + $ident:L = $schema:L.AddMember($name:S, $target:L$traits:W) """, Map.of( + "ident", getMemberSchemaName(shape, member), + "schema", getSchemaName(shape), "name", member.getMemberName(), "target", getSchemaName(target), - "traits", memberTraits.isEmpty() - ? emptyGoTemplate() - : goTemplate(", $W", Writable.map(memberTraits, this::renderVariadicTrait)) + "traits", renderVariadicTraits(memberTraits) )); } + private Writable renderVariadicTraits(java.util.Collection traits) { + if (traits.isEmpty()) { + return emptyGoTemplate(); + } + return goTemplate(", $W", Writable.map(traits, this::renderVariadicTrait)); + } + private Writable renderTraitMapEntry(Trait trait) { var generator = DefaultTraitGenerators.forTrait(trait.toShapeId()); return generator != null diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java index 4c93bc7d0..b3803ed71 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java @@ -134,7 +134,8 @@ private Writable generateMetadata() { return goTemplate(""" const ServiceID = $S const ServiceAPIVersion = $S - """, serviceId, service.getVersion()); + const serviceName = $S + """, serviceId, service.getVersion(), service.getId().getName()); } private Writable generateClient() { @@ -196,6 +197,8 @@ func New(options $options:L, optFns ...func(*$options:L)) *$client:L { $protocolResolvers:W + $experimentalSerdeResolvers:W + for _, fn := range optFns { fn(&options) } @@ -243,10 +246,17 @@ func New(options $options:L, optFns ...func(*$options:L)) *$client:L { .flatMap(it -> it.getClientMemberResolvers().stream()) .map(this::generateClientMemberResolver) .toList() - ).compose() + ).compose(), + "experimentalSerdeResolvers", settings.useExperimentalSerde()? generateExperimentalSerdeResolvers() : emptyGoTemplate() )); } + private Writable generateExperimentalSerdeResolvers() { + ensureSupportedProtocol(); + // TODO(serde2) dynamically resolve a symbol based on traits + return goTemplate("options.Protocol = $T()", SmithyGoDependency.SMITHY_AWS_PROTOCOLS_JSON10.func("New")); + } + private Writable generateGetOptions() { var docs = autoDocTemplate(""" Options returns a copy of the client configuration. @@ -353,6 +363,7 @@ private Writable generateInvokeOperation() { ) { ctx = middleware.ClearStackValues(ctx) ctx = middleware.WithServiceID(ctx, ServiceID) + ctx = middleware.WithServiceName(ctx, serviceName) ctx = middleware.WithOperationName(ctx, opID) $newStack:W diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java index 69c8d2fd1..df88a7041 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java @@ -81,6 +81,9 @@ public final class SmithyGoDependency { public static final GoDependency SMITHY_TRACING = smithy("tracing"); public static final GoDependency SMITHY_METRICS = smithy("metrics"); + public static final GoDependency SMITHY_AWS_PROTOCOLS = relativePackage( "github.com/aws/smithy-go/aws-protocols", null, "v1.0.0", null); + public static final GoDependency SMITHY_AWS_PROTOCOLS_JSON10 = relativePackage( "github.com/aws/smithy-go/aws-protocols", "awsjson10", "v1.0.0", null); + public static final GoDependency MATH = stdlib("math"); private static final String SMITHY_SOURCE_PATH = "github.com/aws/smithy-go"; diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java index 6f6a2d873..773c1ea96 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java @@ -15,22 +15,20 @@ package software.amazon.smithy.go.codegen; -import java.util.Comparator; import java.util.Map; import java.util.Set; import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.codegen.core.SymbolProvider; import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; -import software.amazon.smithy.go.codegen.knowledge.GoPointableIndex; -import software.amazon.smithy.go.codegen.util.ShapeUtil; +import software.amazon.smithy.go.codegen.serde2.StructureDeserializer; +import software.amazon.smithy.go.codegen.serde2.StructureSerializer; import software.amazon.smithy.model.Model; -import software.amazon.smithy.model.shapes.CollectionShape; -import software.amazon.smithy.model.shapes.MapShape; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.ServiceShape; -import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.StructureShape; import software.amazon.smithy.model.traits.ErrorTrait; +import software.amazon.smithy.model.traits.InputTrait; +import software.amazon.smithy.model.traits.OutputTrait; import software.amazon.smithy.model.traits.StreamingTrait; import software.amazon.smithy.utils.MapUtils; import software.amazon.smithy.utils.SetUtils; @@ -57,7 +55,6 @@ public final class StructureGenerator implements Runnable { private final ProtocolGenerator protocolGenerator; private final boolean useExperimentalSerde; private final GoCodegenContext ctx; - private final GoPointableIndex nilIndex; public StructureGenerator( GoCodegenContext ctx, @@ -74,7 +71,6 @@ public StructureGenerator( this.symbol = ctx.symbolProvider().toSymbol(shape); this.protocolGenerator = protocolGenerator; this.useExperimentalSerde = ctx.settings().useExperimentalSerde(); - this.nilIndex = GoPointableIndex.of(model); } public StructureGenerator( @@ -151,7 +147,14 @@ public void renderStructure(Runnable runnable, boolean isInputStructure) { writer.closeBlock("}").write(""); if (useExperimentalSerde) { - // TODO generateSerializers(); + if (shape.hasTrait(InputTrait.class)) { + writer.write(new StructureSerializer(ctx, shape)); + } else if (shape.hasTrait(OutputTrait.class)) { + writer.write(new StructureDeserializer(ctx, shape)); + } else { + writer.write(new StructureSerializer(ctx, shape)); + writer.write(new StructureDeserializer(ctx, shape)); + } } } @@ -214,5 +217,9 @@ private void renderErrorStructure() { fault = "smithy.FaultServer"; } writer.write("func (e *$L) ErrorFault() smithy.ErrorFault { return $L }", structureSymbol.getName(), fault); + + if (useExperimentalSerde) { + writer.write(new StructureDeserializer(ctx, shape)); + } } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java new file mode 100644 index 000000000..7719635b2 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java @@ -0,0 +1,38 @@ +package software.amazon.smithy.go.codegen; + +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + +import java.util.Map; +import software.amazon.smithy.model.shapes.StructureShape; + +public class TypeRegistry implements Writable { + private final GoCodegenContext ctx; + + public TypeRegistry(GoCodegenContext ctx) { + this.ctx = ctx; + } + + @Override + public void accept(GoWriter writer) { + var shapes = ctx.serdeShapes(StructureShape.class).stream().toList(); + + writer.addUseImports(SmithyGoDependency.SMITHY); + writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); + writer.writeGoTemplate(""" + // TypeRegistry is the type registry for this service. + var TypeRegistry = &smithy.TypeRegistry{ + Entries: map[string]*smithy.TypeRegistryEntry{ + $entries:W + }, + } + """, Map.of("entries", Writable.map(shapes, this::renderEntry))); + } + + private Writable renderEntry(StructureShape shape) { + return goTemplate(""" + $S: &smithy.TypeRegistryEntry{ + Schema: schemas.$L, + New: func() any { return &$T{} }, + },""", shape.getId().toString(), SchemaGenerator.getSchemaName(shape), ctx.symbolProvider().toSymbol(shape)); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java index b3faf9dae..d04147520 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java @@ -20,6 +20,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; +import software.amazon.smithy.codegen.core.CodegenException; import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.codegen.core.SymbolProvider; import software.amazon.smithy.model.Model; @@ -38,12 +39,14 @@ public class UnionGenerator { public static final String UNKNOWN_MEMBER_NAME = "UnknownUnionMember"; + private final GoCodegenContext ctx; private final Model model; private final SymbolProvider symbolProvider; private final UnionShape shape; private final boolean isEventStream; - public UnionGenerator(Model model, SymbolProvider symbolProvider, UnionShape shape) { + public UnionGenerator(GoCodegenContext ctx, Model model, SymbolProvider symbolProvider, UnionShape shape) { + this.ctx = ctx; this.model = model; this.symbolProvider = symbolProvider; this.shape = shape; @@ -99,8 +102,75 @@ public void generateUnion(GoWriter writer) { }); writer.write("func (*$L) is$L() {}", exportedMemberName, symbol.getName()); + + if (ctx.settings().useExperimentalSerde()) { + generateMemberSerializer(writer, member, exportedMemberName, target); + generateMemberDeserializer(writer, member, exportedMemberName, target); + } } } + + private void generateMemberSerializer(GoWriter writer, MemberShape member, String memberName, Shape target) { + writer.addUseImports(SmithyGoDependency.SMITHY); + writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); + var schemaName = "schemas." + SchemaGenerator.getMemberSchemaName(shape, member); + writer.openBlock("func (v *$L) Serialize(s smithy.ShapeSerializer) {", "}", memberName, () -> { + switch (target.getType()) { + case BYTE -> writer.write("s.WriteInt8($L, v.Value)", schemaName); + case SHORT -> writer.write("s.WriteInt16($L, v.Value)", schemaName); + case INTEGER -> writer.write("s.WriteInt32($L, v.Value)", schemaName); + case LONG -> writer.write("s.WriteInt64($L, v.Value)", schemaName); + case FLOAT -> writer.write("s.WriteFloat32($L, v.Value)", schemaName); + case DOUBLE -> writer.write("s.WriteFloat64($L, v.Value)", schemaName); + case BOOLEAN -> writer.write("s.WriteBool($L, v.Value)", schemaName); + case STRING -> writer.write("s.WriteString($L, v.Value)", schemaName); + case BLOB -> writer.write("s.WriteBlob($L, v.Value)", schemaName); + case TIMESTAMP -> writer.write("s.WriteTime($L, v.Value)", schemaName); + case ENUM -> writer.write("s.WriteString($L, string(v.Value))", schemaName); + case INT_ENUM -> writer.write("s.WriteInt32($L, int32(v.Value))", schemaName); + case BIG_INTEGER -> writer.write("s.WriteBigInteger($L, v.Value)", schemaName); + case BIG_DECIMAL -> writer.write("s.WriteBigDecimal($L, v.Value)", schemaName); + case STRUCTURE -> writer.write("s.WriteStruct($L, &v.Value)", schemaName); // struct variants are value types + case LIST, SET, MAP -> writer.write("serialize$L(s, $L, v.Value)", target.getId().getName(), schemaName); + case MEMBER, SERVICE, RESOURCE, OPERATION -> throw new CodegenException("invalid shape type " + target.getType()); + } + }); + } + + private void generateMemberDeserializer(GoWriter writer, MemberShape member, String memberName, Shape target) { + writer.addUseImports(SmithyGoDependency.SMITHY); + writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); + var schemaName = "schemas." + SchemaGenerator.getMemberSchemaName(shape, member); + writer.openBlock("func (v *$L) Deserialize(d smithy.ShapeDeserializer) error {", "}", memberName, () -> { + switch (target.getType()) { + case BYTE -> writer.write("return d.ReadInt8($L, &v.Value)", schemaName); + case SHORT -> writer.write("return d.ReadInt16($L, &v.Value)", schemaName); + case INTEGER -> writer.write("return d.ReadInt32($L, &v.Value)", schemaName); + case LONG -> writer.write("return d.ReadInt64($L, &v.Value)", schemaName); + case FLOAT -> writer.write("return d.ReadFloat32($L, &v.Value)", schemaName); + case DOUBLE -> writer.write("return d.ReadFloat64($L, &v.Value)", schemaName); + case BOOLEAN -> writer.write("return d.ReadBool($L, &v.Value)", schemaName); + case STRING -> writer.write("return d.ReadString($L, &v.Value)", schemaName); + case BLOB -> writer.write("return d.ReadBlob($L, &v.Value)", schemaName); + case TIMESTAMP -> writer.write("return d.ReadTime($L, &v.Value)", schemaName); + case ENUM -> { + writer.write("var s string"); + writer.write("if err := d.ReadString($L, &s); err != nil { return err }", schemaName); + writer.write("v.Value = $T(s)", symbolProvider.toSymbol(target)); + writer.write("return nil"); + } + case INT_ENUM -> { + writer.write("var i int32"); + writer.write("if err := d.ReadInt32($L, &i); err != nil { return err }", schemaName); + writer.write("v.Value = $T(i)", symbolProvider.toSymbol(target)); + writer.write("return nil"); + } + case STRUCTURE -> writer.write("return v.Value.Deserialize(d)"); + case LIST, MAP, UNION -> writer.write("return deserialize$L(d, $L, &v.Value)", target.getId().getName(), schemaName); + case MEMBER, SERVICE, RESOURCE, OPERATION -> throw new CodegenException("invalid shape type " + target.getType()); + } + }); + } private boolean isEventStreamErrorMember(MemberShape memberShape) { return isEventStream && memberShape.getMemberTrait(model, ErrorTrait.class).isPresent(); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java new file mode 100644 index 000000000..062d02f9e --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java @@ -0,0 +1,154 @@ +package software.amazon.smithy.go.codegen.serde2; + +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + +import java.util.Map; +import software.amazon.smithy.codegen.core.CodegenException; +import software.amazon.smithy.go.codegen.GoCodegenContext; +import software.amazon.smithy.go.codegen.GoUniverseTypes; +import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.SmithyGoDependency; +import software.amazon.smithy.go.codegen.Writable; +import software.amazon.smithy.go.codegen.util.ShapeUtil; +import software.amazon.smithy.model.shapes.ListShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.traits.SparseTrait; + +public class ListDeserializer implements Writable { + private final GoCodegenContext ctx; + private final ListShape shape; + private final Shape member; + + public ListDeserializer(GoCodegenContext ctx, ListShape shape) { + this.ctx = ctx; + this.shape = shape; + this.member = ShapeUtil.expectMember(ctx.model(), shape); + } + + @Override + public void accept(GoWriter writer) { + writer.addUseImports(SmithyGoDependency.SMITHY); + if (member.hasTrait(SparseTrait.class)) { + renderSparse(writer); + } else { + renderDense(writer); + } + } + + private void renderDense(GoWriter writer) { + writer.writeGoTemplate(""" + func deserialize$shapeName:L(d smithy.ShapeDeserializer, s *smithy.Schema, v *$symbol:T) error { + return smithy.ReadList(d, s, func() error { + var vv $memberSymbol:T + if err := $deserializeMember:W; err != nil { + return err + } + + *v = append(*v, $cast:W) + return nil + }) + } + """, Map.of( + "shapeName", shape.getId().getName(), + "symbol", ctx.symbolProvider().toSymbol(shape), + "memberSymbol", switch (member.getType()) { + case ENUM -> GoUniverseTypes.String; + case INT_ENUM -> GoUniverseTypes.Int32; + default -> ctx.symbolProvider().toSymbol(member); + }, + "deserializeMember", renderDeserializeMember(), + "cast", switch (member.getType()) { + case ENUM, INT_ENUM -> goTemplate("$T(vv)", ctx.symbolProvider().toSymbol(member)); + default -> goTemplate("vv"); + } + )); + } + + private void renderSparse(GoWriter writer) { + writer.writeGoTemplate(""" + func deserialize$shapeName:L(d smithy.ShapeDeserializer, s *smithy.Schema, v *$symbol:T) error { + return smithy.ReadList(d, s, func() error { + if isNil, err := d.ReadNil(s.ListMember()); err != nil { + return err + } else if isNil { + *v = append(*v, nil) + return nil + } + + var vv $memberSymbol:T + if err := $deserializeMember:W; err != nil { + return err + } + + *v = append(*v, $cast:W) + return nil + }) + } + """, Map.of( + "shapeName", shape.getId().getName(), + "symbol", ctx.symbolProvider().toSymbol(shape), + "memberSymbol", switch (member.getType()) { + case ENUM -> GoUniverseTypes.String; + case INT_ENUM -> GoUniverseTypes.Int32; + default -> ctx.symbolProvider().toSymbol(member); + }, + "deserializeMember", renderDeserializeMember(), + "cast", renderSparseCast() + )); + } + + private Writable renderSparseCast() { + return switch (member.getType()) { + case ENUM, INT_ENUM -> goTemplate(""" + func() $T { + ev := $T(vv) + return &ev + }()""", ctx.symbolProvider().toSymbol(member)); + + // don't need the address-of + case BLOB, LIST, SET, MAP, UNION, DOCUMENT -> + goTemplate("vv"); + + default -> goTemplate("&vv"); + }; + } + + private Writable renderDeserializeMember() { + return switch (member.getType()) { + case BYTE -> + goTemplate("d.ReadInt8(s.ListMember(), &vv)"); + case SHORT -> + goTemplate("d.ReadInt16(s.ListMember(), &vv)"); + case INTEGER, INT_ENUM -> + goTemplate("d.ReadInt32(s.ListMember(), &vv)"); + case LONG -> + goTemplate("d.ReadInt64(s.ListMember(), &vv)"); + + case FLOAT -> + goTemplate("d.ReadFloat32(s.ListMember(), &vv)"); + case DOUBLE -> + goTemplate("d.ReadFloat64(s.ListMember(), &vv)"); + + case STRING, ENUM -> + goTemplate("d.ReadString(s.ListMember(), &vv)"); + case BOOLEAN -> + goTemplate("d.ReadBool(s.ListMember(), &vv)"); + case TIMESTAMP -> + goTemplate("d.ReadTimestamp(s.ListMember(), &vv)"); + case BLOB -> + goTemplate("d.ReadBlob(s.ListMember(), &vv)"); + + case LIST, SET, MAP, UNION -> + goTemplate("deserialize$L(d, s.ListMember(), &vv)", member.getId().getName()); + case STRUCTURE -> + goTemplate("vv.Deserialize(d)"); + case DOCUMENT -> + goTemplate("d.ReadDocument(s.ListMember(), &vv)"); + + case BIG_INTEGER, BIG_DECIMAL -> + throw new CodegenException("big integer / big decimal unsupported"); + case MEMBER, OPERATION, RESOURCE, SERVICE -> + throw new CodegenException("invalid shape type " + shape.getType()); + }; + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java new file mode 100644 index 000000000..864cd9ed0 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java @@ -0,0 +1,142 @@ +package software.amazon.smithy.go.codegen.serde2; + +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + +import java.util.Map; +import software.amazon.smithy.codegen.core.CodegenException; +import software.amazon.smithy.go.codegen.GoCodegenContext; +import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.SmithyGoDependency; +import software.amazon.smithy.go.codegen.Writable; +import software.amazon.smithy.go.codegen.util.ShapeUtil; +import software.amazon.smithy.model.shapes.ListShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.traits.SparseTrait; + +public class ListSerializer implements Writable { + private final GoCodegenContext ctx; + private final ListShape shape; + private final Shape member; + + public ListSerializer(GoCodegenContext ctx, ListShape shape) { + this.ctx = ctx; + this.shape = shape; + this.member = ShapeUtil.expectMember(ctx.model(), shape); + } + + @Override + public void accept(GoWriter writer) { + writer.addUseImports(SmithyGoDependency.SMITHY); + writer.writeGoTemplate(""" + func serialize$shapeName:L(s smithy.ShapeSerializer, schema *smithy.Schema, v $symbol:T) { + s.WriteList(schema) + for _, vv := range v { + $serializeValue:W + } + s.CloseList() + } + """, Map.of( + "shapeName", shape.getId().getName(), + "symbol", ctx.symbolProvider().toSymbol(shape), + "serializeValue", renderSerializeValue() + )); + } + + private Writable renderSerializeValue() { + if (shape.hasTrait(SparseTrait.class)) { + return renderSparseSerializeValue(); + } + return renderDenseSerializeValue(); + } + + private Writable wrapNilCheck(Writable inner) { + return goTemplate(""" + if vv != nil { + $W + } else { + s.WriteNil(schema.ListMember()) + }""", inner); + } + + private Writable renderSparseSerializeValue() { + return switch (member.getType()) { + case BYTE -> + wrapNilCheck(goTemplate("s.WriteInt8(schema.ListMember(), *vv)")); + case SHORT -> + wrapNilCheck(goTemplate("s.WriteInt16(schema.ListMember(), *vv)")); + case INTEGER -> + wrapNilCheck(goTemplate("s.WriteInt32(schema.ListMember(), *vv)")); + case INT_ENUM -> + wrapNilCheck(goTemplate("s.WriteInt32(schema.ListMember(), int32(*vv))")); + case LONG -> + wrapNilCheck(goTemplate("s.WriteInt64(schema.ListMember(), *vv)")); + + case FLOAT -> + wrapNilCheck(goTemplate("s.WriteFloat32(schema.ListMember(), *vv)")); + case DOUBLE -> + wrapNilCheck(goTemplate("s.WriteFloat64(schema.ListMember(), *vv)")); + + case STRING -> + wrapNilCheck(goTemplate("s.WriteString(schema.ListMember(), *vv)")); + case ENUM -> + wrapNilCheck(goTemplate("s.WriteString(schema.ListMember(), string(*vv))")); + case BOOLEAN -> + wrapNilCheck(goTemplate("s.WriteBool(schema.ListMember(), *vv)")); + case TIMESTAMP -> + wrapNilCheck(goTemplate("s.WriteTime(schema.ListMember(), *vv)")); + case BLOB -> + wrapNilCheck(goTemplate("s.WriteBlob(schema.ListMember(), vv)")); + + case LIST, SET, MAP, UNION -> + wrapNilCheck(goTemplate("serialize$L(s, schema.ListMember(), vv)", member.getId().getName())); + case STRUCTURE -> + wrapNilCheck(goTemplate("vv.Serialize(s)")); + case DOCUMENT -> + wrapNilCheck(goTemplate("s.WriteDocument(schema.ListMember(), vv)")); + + case BIG_INTEGER, BIG_DECIMAL -> + throw new CodegenException("big integer / big decimal unsupported"); + case MEMBER, OPERATION, RESOURCE, SERVICE -> + throw new CodegenException("invalid shape type " + member.getType()); + }; + } + + private Writable renderDenseSerializeValue() { + return switch (member.getType()) { + case BYTE -> + goTemplate("s.WriteInt8(schema.ListMember(), vv)"); + case SHORT -> + goTemplate("s.WriteInt16(schema.ListMember(), vv)"); + case INTEGER, INT_ENUM -> + goTemplate("s.WriteInt32(schema.ListMember(), int32(vv))"); + case LONG -> + goTemplate("s.WriteInt64(schema.ListMember(), vv)"); + + case FLOAT -> + goTemplate("s.WriteFloat32(schema.ListMember(), vv)"); + case DOUBLE -> + goTemplate("s.WriteFloat64(schema.ListMember(), vv)"); + + case STRING, ENUM -> + goTemplate("s.WriteString(schema.ListMember(), string(vv))"); + case BOOLEAN -> + goTemplate("s.WriteBool(schema.ListMember(), vv)"); + case TIMESTAMP -> + goTemplate("s.WriteTime(schema.ListMember(), vv)"); + case BLOB -> + goTemplate("s.WriteBlob(schema.ListMember(), vv)"); + + case LIST, SET, MAP, UNION -> + goTemplate("serialize$L(s, schema.ListMember(), vv)", member.getId().getName()); + case STRUCTURE -> + goTemplate("vv.Serialize(s)"); + case DOCUMENT -> + goTemplate("s.WriteDocument(schema.ListMember(), vv)"); + + case BIG_INTEGER, BIG_DECIMAL -> + throw new CodegenException("big integer / big decimal unsupported"); + case MEMBER, OPERATION, RESOURCE, SERVICE -> + throw new CodegenException("invalid shape type " + member.getType()); + }; + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java new file mode 100644 index 000000000..36b7f5ecc --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java @@ -0,0 +1,156 @@ +package software.amazon.smithy.go.codegen.serde2; + +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + +import java.util.Map; +import software.amazon.smithy.codegen.core.CodegenException; +import software.amazon.smithy.go.codegen.GoCodegenContext; +import software.amazon.smithy.go.codegen.GoUniverseTypes; +import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.SmithyGoDependency; +import software.amazon.smithy.go.codegen.Writable; +import software.amazon.smithy.go.codegen.util.ShapeUtil; +import software.amazon.smithy.model.shapes.MapShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.traits.SparseTrait; + +public class MapDeserializer implements Writable { + private final GoCodegenContext ctx; + private final MapShape shape; + private final Shape value; + + public MapDeserializer(GoCodegenContext ctx, MapShape shape) { + this.ctx = ctx; + this.shape = shape; + this.value = ShapeUtil.expectMember(ctx.model(), shape); + } + + @Override + public void accept(GoWriter writer) { + writer.addUseImports(SmithyGoDependency.SMITHY); + if (shape.hasTrait(SparseTrait.class)) { + renderSparse(writer); + } else { + renderDense(writer); + } + } + + private void renderDense(GoWriter writer) { + writer.writeGoTemplate(""" + func deserialize$shapeName:L(d smithy.ShapeDeserializer, s *smithy.Schema, v *$symbol:T) error { + *v = make($symbol:T) + return smithy.ReadMap(d, s, func(k string) error { + var vv $valueSymbol:T + if err := $deserializeValue:W; err != nil { + return err + } + + (*v)[k] = $cast:W + return nil + }) + } + """, Map.of( + "shapeName", shape.getId().getName(), + "symbol", ctx.symbolProvider().toSymbol(shape), + "valueSymbol", switch (value.getType()) { + case ENUM -> GoUniverseTypes.String; + case INT_ENUM -> GoUniverseTypes.Int32; + default -> ctx.symbolProvider().toSymbol(value); + }, + "deserializeValue", renderDeserializeValue(), + "cast", switch (value.getType()) { + case ENUM, INT_ENUM -> goTemplate("$T(vv)", ctx.symbolProvider().toSymbol(value)); + default -> goTemplate("vv"); + } + )); + } + + private void renderSparse(GoWriter writer) { + writer.writeGoTemplate(""" + func deserialize$shapeName:L(d smithy.ShapeDeserializer, s *smithy.Schema, v *$symbol:T) error { + *v = make($symbol:T) + return smithy.ReadMap(d, s, func(k string) error { + if isNil, err := d.ReadNil(s.MapValue()); err != nil { + return err + } else if isNil { + (*v)[k] = nil + return nil + } + + var vv $valueSymbol:T + if err := $deserializeValue:W; err != nil { + return err + } + + (*v)[k] = $cast:W + return nil + }) + } + """, Map.of( + "shapeName", shape.getId().getName(), + "symbol", ctx.symbolProvider().toSymbol(shape), + "valueSymbol", switch (value.getType()) { + case ENUM -> GoUniverseTypes.String; + case INT_ENUM -> GoUniverseTypes.Int32; + default -> ctx.symbolProvider().toSymbol(value); + }, + "deserializeValue", renderDeserializeValue(), + "cast", renderSparseCast() + )); + } + + private Writable renderSparseCast() { + return switch (value.getType()) { + case ENUM, INT_ENUM -> goTemplate(""" + func() $T { + ev := $T(vv) + return &ev + }()""", ctx.symbolProvider().toSymbol(value)); + + // don't need the address-of + case BLOB, LIST, SET, MAP, UNION, DOCUMENT -> + goTemplate("vv"); + + default -> goTemplate("&vv"); + }; + } + + private Writable renderDeserializeValue() { + return switch (value.getType()) { + case BYTE -> + goTemplate("d.ReadInt8(s.MapValue(), &vv)"); + case SHORT -> + goTemplate("d.ReadInt16(s.MapValue(), &vv)"); + case INTEGER, INT_ENUM -> + goTemplate("d.ReadInt32(s.MapValue(), &vv)"); + case LONG -> + goTemplate("d.ReadInt64(s.MapValue(), &vv)"); + + case FLOAT -> + goTemplate("d.ReadFloat32(s.MapValue(), &vv)"); + case DOUBLE -> + goTemplate("d.ReadFloat64(s.MapValue(), &vv)"); + + case STRING, ENUM -> + goTemplate("d.ReadString(s.MapValue(), &vv)"); + case BOOLEAN -> + goTemplate("d.ReadBool(s.MapValue(), &vv)"); + case TIMESTAMP -> + goTemplate("d.ReadTime(s.MapValue(), &vv)"); + case BLOB -> + goTemplate("d.ReadBlob(s.MapValue(), &vv)"); + + case LIST, SET, MAP, UNION -> + goTemplate("deserialize$L(d, s.MapValue(), &vv)", value.getId().getName()); + case STRUCTURE -> + goTemplate("vv.Deserialize(d)"); + case DOCUMENT -> + goTemplate("d.ReadDocument(s.MapValue(), &vv)"); + + case BIG_INTEGER, BIG_DECIMAL -> + throw new CodegenException("big integer / big decimal unsupported"); + case MEMBER, OPERATION, RESOURCE, SERVICE -> + throw new CodegenException("invalid shape type " + value.getType()); + }; + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java new file mode 100644 index 000000000..be4953f7a --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java @@ -0,0 +1,143 @@ +package software.amazon.smithy.go.codegen.serde2; + +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + +import java.util.Map; +import software.amazon.smithy.codegen.core.CodegenException; +import software.amazon.smithy.go.codegen.GoCodegenContext; +import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.SmithyGoDependency; +import software.amazon.smithy.go.codegen.Writable; +import software.amazon.smithy.go.codegen.util.ShapeUtil; +import software.amazon.smithy.model.shapes.MapShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.traits.SparseTrait; + +public class MapSerializer implements Writable { + private final GoCodegenContext ctx; + private final MapShape shape; + private final Shape value; + + public MapSerializer(GoCodegenContext ctx, MapShape shape) { + this.ctx = ctx; + this.shape = shape; + this.value = ShapeUtil.expectMember(ctx.model(), shape); + } + + @Override + public void accept(GoWriter writer) { + writer.addUseImports(SmithyGoDependency.SMITHY); + writer.writeGoTemplate(""" + func serialize$shapeName:L(s smithy.ShapeSerializer, schema *smithy.Schema, v $symbol:T) { + s.WriteMap(schema) + for k, vv := range v { + s.WriteKey(schema.MapKey(), k) + $serializeValue:W + } + s.CloseMap() + } + """, Map.of( + "shapeName", shape.getId().getName(), + "symbol", ctx.symbolProvider().toSymbol(shape), + "serializeValue", renderSerializeValue() + )); + } + + private Writable renderSerializeValue() { + if (shape.hasTrait(SparseTrait.class)) { + return renderSparseSerializeValue(); + } + return renderDenseSerializeValue(); + } + + private Writable wrapNilCheck(Writable inner) { + return goTemplate(""" + if vv != nil { + $W + } else { + s.WriteNil(schema.MapValue()) + }""", inner); + } + + private Writable renderSparseSerializeValue() { + return switch (value.getType()) { + case BYTE -> + wrapNilCheck(goTemplate("s.WriteInt8(schema.MapValue(), *vv)")); + case SHORT -> + wrapNilCheck(goTemplate("s.WriteInt16(schema.MapValue(), *vv)")); + case INTEGER -> + wrapNilCheck(goTemplate("s.WriteInt32(schema.MapValue(), *vv)")); + case INT_ENUM -> + wrapNilCheck(goTemplate("s.WriteInt32(schema.MapValue(), int32(*vv))")); + case LONG -> + wrapNilCheck(goTemplate("s.WriteInt64(schema.MapValue(), *vv)")); + + case FLOAT -> + wrapNilCheck(goTemplate("s.WriteFloat32(schema.MapValue(), *vv)")); + case DOUBLE -> + wrapNilCheck(goTemplate("s.WriteFloat64(schema.MapValue(), *vv)")); + + case STRING -> + wrapNilCheck(goTemplate("s.WriteString(schema.MapValue(), *vv)")); + case ENUM -> + wrapNilCheck(goTemplate("s.WriteString(schema.MapValue(), string(*vv))")); + case BOOLEAN -> + wrapNilCheck(goTemplate("s.WriteBool(schema.MapValue(), *vv)")); + case TIMESTAMP -> + wrapNilCheck(goTemplate("s.WriteTime(schema.MapValue(), *vv)")); + case BLOB -> + wrapNilCheck(goTemplate("s.WriteBlob(schema.MapValue(), vv)")); + + case LIST, SET, MAP, UNION -> + wrapNilCheck(goTemplate("serialize$L(s, schema.MapValue(), vv)", value.getId().getName())); + case STRUCTURE -> + wrapNilCheck(goTemplate("vv.Serialize(s)")); + case DOCUMENT -> + wrapNilCheck(goTemplate("s.WriteDocument(schema.MapValue(), vv)")); + + case BIG_INTEGER, BIG_DECIMAL -> + throw new CodegenException("big integer / big decimal unsupported"); + case MEMBER, OPERATION, RESOURCE, SERVICE -> + throw new CodegenException("invalid shape type " + value.getType()); + }; + } + + private Writable renderDenseSerializeValue() { + return switch (value.getType()) { + case BYTE -> + goTemplate("s.WriteInt8(schema.MapValue(), vv)"); + case SHORT -> + goTemplate("s.WriteInt16(schema.MapValue(), vv)"); + case INTEGER, INT_ENUM -> + goTemplate("s.WriteInt32(schema.MapValue(), int32(vv))"); + case LONG -> + goTemplate("s.WriteInt64(schema.MapValue(), vv)"); + + case FLOAT -> + goTemplate("s.WriteFloat32(schema.MapValue(), vv)"); + case DOUBLE -> + goTemplate("s.WriteFloat64(schema.MapValue(), vv)"); + + case STRING, ENUM -> + goTemplate("s.WriteString(schema.MapValue(), string(vv))"); + case BOOLEAN -> + goTemplate("s.WriteBool(schema.MapValue(), vv)"); + case TIMESTAMP -> + goTemplate("s.WriteTime(schema.MapValue(), vv)"); + case BLOB -> + goTemplate("s.WriteBlob(schema.MapValue(), vv)"); + + case LIST, SET, MAP, UNION -> + goTemplate("serialize$L(s, schema.MapValue(), vv)", value.getId().getName()); + case STRUCTURE -> + goTemplate("vv.Serialize(s)"); + case DOCUMENT -> + goTemplate("s.WriteDocument(schema.MapValue(), vv)"); + + case BIG_INTEGER, BIG_DECIMAL -> + throw new CodegenException("big integer / big decimal unsupported"); + case MEMBER, OPERATION, RESOURCE, SERVICE -> + throw new CodegenException("invalid shape type " + value.getType()); + }; + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2DeserializeResponseMiddleware.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2DeserializeResponseMiddleware.java new file mode 100644 index 000000000..4763a6ccd --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2DeserializeResponseMiddleware.java @@ -0,0 +1,57 @@ +package software.amazon.smithy.go.codegen.serde2; + +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; +import static software.amazon.smithy.go.codegen.SymbolUtils.buildPackageSymbol; +import static software.amazon.smithy.go.codegen.SymbolUtils.pointerTo; + +import java.util.Map; +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.go.codegen.SmithyGoDependency; +import software.amazon.smithy.go.codegen.Writable; +import software.amazon.smithy.go.codegen.middleware.DeserializeStepMiddleware; + +public class Serde2DeserializeResponseMiddleware extends DeserializeStepMiddleware { + @Override + public String getStructName() { + return "deserializeResponseMiddleware"; + } + + @Override + public String getId() { + return "OperationDeserializer"; + } + + @Override + public Map getFields() { + return Map.of( + "options", pointerTo(buildPackageSymbol("Options")), + "output", SmithyGoDependency.SMITHY.interfaceSymbol("Deserializable") + ); + } + + @Override + public Writable getFuncBody() { + return goTemplate(""" + out, md, err := next.HandleDeserialize(ctx, in) + if err != nil { + return out, md, err + } + + resp, ok := out.RawResponse.(*smithyhttp.Response) + if !ok { + return out, md, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)} + } + + _, span := tracing.StartSpan(ctx, "OperationDeserializer") + endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") + + err = m.options.Protocol.DeserializeResponse(ctx, TypeRegistry, resp, m.output) + out.Result = m.output + + endTimer() + span.End() + + return out, md, err + """); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2SerializeRequestMiddleware.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2SerializeRequestMiddleware.java new file mode 100644 index 000000000..c5608ecbb --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2SerializeRequestMiddleware.java @@ -0,0 +1,61 @@ +package software.amazon.smithy.go.codegen.serde2; + +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; +import static software.amazon.smithy.go.codegen.SymbolUtils.buildPackageSymbol; +import static software.amazon.smithy.go.codegen.SymbolUtils.pointerTo; + +import java.util.Map; +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.SmithyGoDependency; +import software.amazon.smithy.go.codegen.Writable; +import software.amazon.smithy.go.codegen.middleware.SerializeStepMiddleware; + +public class Serde2SerializeRequestMiddleware extends SerializeStepMiddleware { + @Override + public String getStructName() { + return "serializeRequestMiddleware"; + } + + @Override + public String getId() { + return "OperationSerializer"; + } + + @Override + public Map getFields() { + return Map.of( + "options", pointerTo(buildPackageSymbol("Options")) + ); + } + + @Override + public Writable getFuncBody() { + return GoWriter.goTemplate(""" + $D + req, ok := in.Request.(*smithyhttp.Request) + if !ok { + return middleware.SerializeOutput{}, middleware.Metadata{}, fmt.Errorf("unexpected transport type %T", in.Request) + } + + input, ok := in.Parameters.(smithy.Serializable) + if !ok { + return middleware.SerializeOutput{}, middleware.Metadata{}, fmt.Errorf("input %T is not Serializable", in.Request) + } + + _, span := tracing.StartSpan(ctx, "OperationSerializer") + endTimer := startMetricTimer(ctx, "client.call.serialization_duration") + + err := m.options.Protocol.SerializeRequest(ctx, input, req) + + endTimer() + span.End() + + if err != nil { + return middleware.SerializeOutput{}, middleware.Metadata{}, err + } + + return next.HandleSerialize(ctx, in) + """, SmithyGoDependency.SMITHY_TRACING); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java new file mode 100644 index 000000000..9ed2c6343 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java @@ -0,0 +1,127 @@ +package software.amazon.smithy.go.codegen.serde2; + +import java.util.Comparator; +import software.amazon.smithy.go.codegen.GoCodegenContext; +import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.ProtocolDocumentGenerator; +import software.amazon.smithy.go.codegen.SchemaGenerator; +import software.amazon.smithy.go.codegen.SmithyGoDependency; +import software.amazon.smithy.go.codegen.SmithyGoTypes; +import software.amazon.smithy.go.codegen.UnsupportedShapeException; +import software.amazon.smithy.go.codegen.Writable; +import software.amazon.smithy.go.codegen.knowledge.GoPointableIndex; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.StructureShape; + +public class StructureDeserializer implements Writable { + private final GoCodegenContext ctx; + private final StructureShape shape; + private final GoPointableIndex nilIndex; + + public StructureDeserializer(GoCodegenContext ctx, StructureShape shape) { + this.ctx = ctx; + this.shape = shape; + + this.nilIndex = GoPointableIndex.of(ctx.model()); + } + + @Override + public void accept(GoWriter writer) { + writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); + writer.addUseImports(SmithyGoDependency.SMITHY); + + var symbol = ctx.symbolProvider().toSymbol(shape); + var members = shape.members().stream() + .sorted(Comparator.comparing(MemberShape::getMemberName)) + .toList(); + writer.openBlock("func (v *$L) Deserialize(d smithy.ShapeDeserializer) error {", "}", symbol.getName(), () -> { + writer.openBlock("return smithy.ReadStruct(d, schemas.$L, func(s *smithy.Schema) error {", "})", SchemaGenerator.getSchemaName(shape), () -> { + writer.openBlock("switch s {", "}", () -> { + for (var member : members) { + writer.write("case schemas.$L:", SchemaGenerator.getMemberSchemaName(shape, member)); + renderMember(writer, member, ctx.model().expectShape(member.getTarget()), "v." + ctx.symbolProvider().toMemberName(member)); + } + }); + writer.write("return nil"); + }); + }); + } + + private void renderMember(GoWriter writer, MemberShape member, Shape target, String ident) { + var schemaName = SchemaGenerator.getMemberSchemaName(shape, member); + var ptrSuffix = nilIndex.isNillable(member) ? "Ptr" : ""; + switch (target.getType()) { + case BYTE -> + writer.write("return d.ReadInt8$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + case SHORT -> + writer.write("return d.ReadInt16$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + case INTEGER -> + writer.write("return d.ReadInt32$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + case LONG -> + writer.write("return d.ReadInt64$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + + case FLOAT -> + writer.write("return d.ReadFloat32$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + case DOUBLE -> + writer.write("return d.ReadFloat64$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + + case STRING -> + writer.write("return d.ReadString$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + case ENUM -> + writer.write(""" + var ev string + if err := d.ReadString(schemas.$1L, &ev); err != nil { + return err + } + $2L = $3T(ev) + return nil""", schemaName, ident, ctx.symbolProvider().toSymbol(target)); + case INT_ENUM -> + writer.write(""" + var ev int32 + if err := d.ReadInt32(schemas.$1L, &ev); err != nil { + return err + } + $2L = $3T(ev) + return nil""", schemaName, ident, ctx.symbolProvider().toSymbol(target)); + case BOOLEAN -> + writer.write("return d.ReadBool$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + case TIMESTAMP -> + writer.write("return d.ReadTime$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + case BLOB -> + writer.write("return d.ReadBlob(schemas.$L, &$L)", schemaName, ident); + + case LIST, SET, MAP, UNION -> + writer.write("return deserialize$L(d, schemas.$L, &$L)", target.getId().getName(), schemaName, ident); + case STRUCTURE -> { + if (nilIndex.isNillable(member)) { + writer.write(""" + $1L = &$2T{} + return $1L.Deserialize(d)""", ident, ctx.symbolProvider().toSymbol(target)); + } else { + writer.write("return $L.Deserialize(d)", ident); + } + } + case DOCUMENT -> { + var unmarshaler = ProtocolDocumentGenerator.Utilities.getInternalDocumentSymbolBuilder( + ctx.settings(), ProtocolDocumentGenerator.INTERNAL_NEW_DOCUMENT_UNMARSHALER_FUNC).build(); + writer.addUseImports(SmithyGoDependency.SMITHY_DOCUMENT); + writer.write(""" + var dv smithydocument.Value + if err := d.ReadDocument(schemas.$L, &dv); err != nil { + return err + } + if ov, ok := dv.(smithydocument.Opaque); ok { + $L = $T(ov.Value) + } + return nil""", schemaName, ident, unmarshaler); + } + + // FUTURE(602) + case BIG_INTEGER, BIG_DECIMAL -> throw new UnsupportedShapeException(target.getType()); + + // invalid in this context + case MEMBER, SERVICE, RESOURCE, OPERATION -> throw new UnsupportedShapeException(target.getType()); + } + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java new file mode 100644 index 000000000..335dc386c --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java @@ -0,0 +1,99 @@ +package software.amazon.smithy.go.codegen.serde2; + +import java.util.Comparator; +import software.amazon.smithy.codegen.core.CodegenException; +import software.amazon.smithy.go.codegen.GoCodegenContext; +import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.SchemaGenerator; +import software.amazon.smithy.go.codegen.SmithyGoDependency; +import software.amazon.smithy.go.codegen.UnsupportedShapeException; +import software.amazon.smithy.go.codegen.Writable; +import software.amazon.smithy.go.codegen.knowledge.GoPointableIndex; +import software.amazon.smithy.go.codegen.util.ShapeUtil; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.utils.SmithyInternalApi; + +@SmithyInternalApi +public final class StructureSerializer implements Writable { + private final GoCodegenContext ctx; + private final StructureShape shape; + private final GoPointableIndex nilIndex; + + public StructureSerializer(GoCodegenContext ctx, StructureShape shape) { + this.ctx = ctx; + this.shape = shape; + + this.nilIndex = GoPointableIndex.of(ctx.model()); + } + + @Override + public void accept(GoWriter writer) { + writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); + + var symbol = ctx.symbolProvider().toSymbol(shape); + var members = shape.members().stream() + .sorted(Comparator.comparing(MemberShape::getMemberName)) + .toList(); + writer.addUseImports(SmithyGoDependency.SMITHY); + writer.openBlock("func (v *$L) Serialize(s smithy.ShapeSerializer) {", "}", symbol.getName(), () -> { + writer.write("s.WriteMap(schemas.$L)", SchemaGenerator.getSchemaName(shape)); + for (var member : members) { + var target = ShapeUtil.expectMember(ctx.model(), shape, member.getMemberName()); + var ident = String.format("v.%s", ctx.symbolProvider().toMemberName(member)); + generateSerializeMember(writer, member, target, ident); + } + writer.write("s.CloseMap()"); + }); + writer.write(""); + } + + private void generateSerializeMember(GoWriter writer, MemberShape member, Shape target, String ident) { + var schemaName = "schemas." + SchemaGenerator.getMemberSchemaName(shape, member); + var ptrSuffix = nilIndex.isNillable(member) ? "Ptr" : ""; + switch (target.getType()) { + case BYTE -> + writer.write("s.WriteInt8$L($L, $L)", ptrSuffix, schemaName, ident); + case SHORT -> + writer.write("s.WriteInt16$L($L, $L)", ptrSuffix, schemaName, ident); + case INTEGER -> + writer.write("s.WriteInt32$L($L, $L)", ptrSuffix, schemaName, ident); + case LONG -> + writer.write("s.WriteInt64$L($L, $L)", ptrSuffix, schemaName, ident); + + case FLOAT -> + writer.write("s.WriteFloat32$L($L, $L)", ptrSuffix, schemaName, ident); + case DOUBLE -> + writer.write("s.WriteFloat64$L($L, $L)", ptrSuffix, schemaName, ident); + + case STRING -> + writer.write("s.WriteString$L($L, $L)", ptrSuffix, schemaName, ident); + case ENUM -> + writer.write("s.WriteString($L, string($L))", schemaName, ident); + case INT_ENUM -> + writer.write("s.WriteInt32($L, int32($L))", schemaName, ident); + case BOOLEAN -> + writer.write("s.WriteBool$L($L, $L)", ptrSuffix, schemaName, ident); + case TIMESTAMP -> + writer.write("s.WriteTime$L($L, $L)", ptrSuffix, schemaName, ident); + case BLOB -> + writer.write("s.WriteBlob($L, $L)", schemaName, ident); + + case LIST, SET, MAP, UNION -> + writer.write("serialize$L(s, $L, $L)", target.getId().getName(), schemaName, ident); + case STRUCTURE -> + writer.write("if ($2L != nil) { s.WriteStruct($1L, $2L) }", schemaName, ident); + case DOCUMENT -> { + writer.addUseImports(SmithyGoDependency.SMITHY_DOCUMENT); + writer.write("s.WriteDocument($L, &smithydocument.Opaque{Value: $L})", schemaName, ident); + } + + // FUTURE(602) + case BIG_INTEGER, BIG_DECIMAL -> throw new UnsupportedShapeException(target.getType()); + + // invalid in this context + case MEMBER, SERVICE, RESOURCE, OPERATION -> throw new CodegenException("invalid shape " + target.getType()); + } + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java new file mode 100644 index 000000000..938c83a0a --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java @@ -0,0 +1,73 @@ +package software.amazon.smithy.go.codegen.serde2; + +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + +import java.util.Comparator; +import java.util.Map; +import software.amazon.smithy.go.codegen.GoCodegenContext; +import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.SchemaGenerator; +import software.amazon.smithy.go.codegen.SmithyGoDependency; +import software.amazon.smithy.go.codegen.Writable; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.UnionShape; + +public class UnionDeserializer implements Writable { + private final GoCodegenContext ctx; + private final UnionShape shape; + + public UnionDeserializer(GoCodegenContext ctx, UnionShape shape) { + this.ctx = ctx; + this.shape = shape; + } + + @Override + public void accept(GoWriter writer) { + writer.addUseImports(SmithyGoDependency.SMITHY); + writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); + + var symbol = ctx.symbolProvider().toSymbol(shape); + var members = shape.members().stream() + .sorted(Comparator.comparing(MemberShape::getMemberName)) + .toList(); + + writer.writeGoTemplate(""" + func deserialize$shapeName:L(d smithy.ShapeDeserializer, s *smithy.Schema, v *$symbol:T) error { + ms, err := d.ReadUnion(s) + if err != nil { + return err + } + + switch ms { + $cases:W + } + return nil + } + """, Map.of( + "shapeName", shape.getId().getName(), + "symbol", symbol, + "cases", renderCases(members) + )); + } + + private Writable renderCases(java.util.List members) { + return (GoWriter w) -> { + var unionSymbol = ctx.symbolProvider().toSymbol(shape); + for (var member : members) { + var memberName = ctx.symbolProvider().toMemberName(member); + var variantSchema = SchemaGenerator.getMemberSchemaName(shape, member); + + var memberSymbol = unionSymbol.toBuilder() + .name(memberName) + .build(); + + w.write("case schemas.$L:", variantSchema); + w.indent(); + w.write("vv := &$T{}", memberSymbol); + w.write("*v = vv"); + w.write("return vv.Deserialize(d)"); + w.dedent(); + } + }; + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionSerializer.java new file mode 100644 index 000000000..9a5474c61 --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionSerializer.java @@ -0,0 +1,62 @@ +package software.amazon.smithy.go.codegen.serde2; + +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.go.codegen.GoCodegenContext; +import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.SchemaGenerator; +import software.amazon.smithy.go.codegen.SmithyGoDependency; +import software.amazon.smithy.go.codegen.Writable; +import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.UnionShape; + +public class UnionSerializer implements Writable { + private final GoCodegenContext ctx; + private final UnionShape shape; + + public UnionSerializer(GoCodegenContext ctx, UnionShape shape) { + this.ctx = ctx; + this.shape = shape; + } + + @Override + public void accept(GoWriter writer) { + writer.addUseImports(SmithyGoDependency.SMITHY); + writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); + + var symbol = ctx.symbolProvider().toSymbol(shape); + var members = shape.members().stream() + .sorted(Comparator.comparing(MemberShape::getMemberName)) + .toList(); + + writer.writeGoTemplate(""" + func serialize$shapeName:L(s smithy.ShapeSerializer, schema *smithy.Schema, v $symbol:T) { + switch vv := v.(type) { + $cases:W + } + } + """, Map.of( + "shapeName", shape.getId().getName(), + "symbol", symbol, + "cases", renderCases(members) + )); + } + + private Writable renderCases(List members) { + return (GoWriter w) -> { + for (var member : members) { + var variantSymbol = Symbol.builder() + .name(ctx.symbolProvider().toMemberName(member)) + .namespace(ctx.settings().getModuleName() + "/types", ".") + .build(); + var variantSchema = SchemaGenerator.getMemberSchemaName(shape, member); + w.write("case *$T:", variantSymbol); + w.write(" s.WriteUnion(schema, schemas.$L, vv)", variantSchema); + } + }; + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java index 7224d1614..a60c6aea1 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java @@ -28,6 +28,7 @@ import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.ShapeType; import software.amazon.smithy.model.shapes.StringShape; +import software.amazon.smithy.model.shapes.StructureShape; public final class ShapeUtil { public static final StringShape STRING_SHAPE = StringShape.builder() @@ -42,6 +43,10 @@ public final class ShapeUtil { .id("smithy.api#PrimitiveBoolean") .build(); + public static final StructureShape UNIT = StructureShape.builder() + .id("smithy.api#Unit") + .build(); + private ShapeUtil() {} public static boolean isExported(Shape shape) { diff --git a/document/document.go b/document/document.go index 8f852d95c..82b48eb59 100644 --- a/document/document.go +++ b/document/document.go @@ -4,6 +4,7 @@ import ( "fmt" "math/big" "strconv" + "time" ) // Marshaler is an interface for a type that marshals a document to its protocol-specific byte representation and @@ -15,26 +16,26 @@ import ( // When defining struct types. the `document` struct tag can be used to control how the value will be // marshaled into the resulting protocol document. // -// // Field is ignored -// Field int `document:"-"` +// // Field is ignored +// Field int `document:"-"` // -// // Field object of key "myName" -// Field int `document:"myName"` +// // Field object of key "myName" +// Field int `document:"myName"` // -// // Field object key of key "myName", and -// // Field is omitted if the field is a zero value for the type. -// Field int `document:"myName,omitempty"` +// // Field object key of key "myName", and +// // Field is omitted if the field is a zero value for the type. +// Field int `document:"myName,omitempty"` // -// // Field object key of "Field", and -// // Field is omitted if the field is a zero value for the type. -// Field int `document:",omitempty"` +// // Field object key of "Field", and +// // Field is omitted if the field is a zero value for the type. +// Field int `document:",omitempty"` // // All struct fields, including anonymous fields, are marshaled unless the // any of the following conditions are meet. // -// - the field is not exported -// - document field tag is "-" -// - document field tag specifies "omitempty", and is a zero value. +// - the field is not exported +// - document field tag is "-" +// - document field tag specifies "omitempty", and is a zero value. // // Pointer and interface values are encoded as the value pointed to or // contained in the interface. A nil value encodes as a null @@ -50,6 +51,13 @@ import ( // // Marshal cannot represent cyclic data structures and will not handle them. // Passing cyclic structures to Marshal will result in an infinite recursion. +// +// Marshaler is not used in schema-serde based services (which are currently +// being rolled out) since having an implementation of Marshaler locks a +// document into support for a specific serial format. Existing implementations +// of Marshaler will continue to encode to JSON as that is effectively the only +// serial format supported for Document prior to the introduction of +// schema-serde. In schema-serde services it is replaced by [Value]. type Marshaler interface { MarshalSmithyDocument() ([]byte, error) } @@ -63,18 +71,94 @@ type Marshaler interface { // // Both generic interface{} and concrete types are valid unmarshal destination types. When unmarshaling a document // into an empty interface the Unmarshaler will store one of these values: -// bool, for boolean values -// document.Number, for arbitrary-precision numbers (int64, float64, big.Int, big.Float) -// string, for string values -// []interface{}, for array values -// map[string]interface{}, for objects -// nil, for null values +// +// bool, for boolean values +// document.Number, for arbitrary-precision numbers (int64, float64, big.Int, big.Float) +// string, for string values +// []interface{}, for array values +// map[string]interface{}, for objects +// nil, for null values // // When unmarshaling, any error that occurs will halt the unmarshal and return the error. type Unmarshaler interface { UnmarshalSmithyDocument(v interface{}) error } +// Value is a sealed type representing a Smithy document value. It covers the +// full Smithy data model including blob and timestamp. +// +// The following types implement Value: +// - [Null] +// - [Boolean] +// - [Number] +// - [String] +// - [Blob] +// - [Timestamp] +// - [List] +// - [Map] +// - [Structure] +// - [Opaque] +type Value interface { + isValue() +} + +// Null is a document null value. +type Null struct{} + +func (Null) isValue() {} + +// Boolean is a document boolean value. +type Boolean bool + +func (Boolean) isValue() {} + +// String is a document string value. +type String string + +func (String) isValue() {} + +// Blob is a document blob value. +type Blob []byte + +func (Blob) isValue() {} + +// Timestamp is a document timestamp value. +type Timestamp time.Time + +func (Timestamp) isValue() {} + +// List is a document list value. +type List []Value + +func (List) isValue() {} + +// Map is a document map value with string keys. +type Map map[string]Value + +func (Map) isValue() {} + +// Structure is a document structure value with an optional discriminator +// identifying the shape it represents. +type Structure struct { + // Discriminator is the absolute shape ID (e.g. + // "com.example#MyShape") of the concrete type this structure + // represents. It may be empty if the type is unknown. + Discriminator string + + // Members maps member names to their document values. + Members map[string]Value +} + +func (Structure) isValue() {} + +// Opaque wraps an arbitrary Go value for backward compatibility with the +// legacy reflection-based document serialization path. +type Opaque struct { + Value any +} + +func (Opaque) isValue() {} + type noSerde interface { noSmithyDocumentSerde() } @@ -96,6 +180,8 @@ func IsNoSerde(x interface{}) bool { // Number is an arbitrary precision numerical value type Number string +func (Number) isValue() {} + // Int64 returns the number as a string. func (n Number) String() string { return string(n) diff --git a/middleware/context.go b/middleware/context.go index f51aa4f04..f4b03ab37 100644 --- a/middleware/context.go +++ b/middleware/context.go @@ -5,6 +5,7 @@ import "context" type ( serviceIDKey struct{} operationNameKey struct{} + serviceNameKey struct{} ) // WithServiceID adds a service ID to the context, scoped to middleware stack @@ -39,3 +40,19 @@ func GetOperationName(ctx context.Context) string { name, _ := GetStackValue(ctx, operationNameKey{}).(string) return name } + +// WithServiceName adds the service name to the context, scoped to middleware +// stack values. +// +// This API is called in the client runtime when bootstrapping an operation and +// should not typically be used directly. +func WithServiceName(parent context.Context, id string) context.Context { + return WithStackValue(parent, serviceNameKey{}, id) +} + +// GetServiceName retrieves the service name from the context. This is +// ALWAYS the service shape's name from its Smithy model. +func GetServiceName(ctx context.Context) string { + name, _ := GetStackValue(ctx, serviceNameKey{}).(string) + return name +} diff --git a/schema.go b/schema.go index 6d92c2d97..7cb55bab7 100644 --- a/schema.go +++ b/schema.go @@ -1,6 +1,7 @@ package smithy import ( + "fmt" "maps" "strings" ) @@ -42,6 +43,14 @@ type ShapeID struct { Namespace, Name, Member string } +// String returns the IDL microformat for the shape ID. +func (s *ShapeID) String() string { + if s.Member == "" { + return fmt.Sprintf("%s#%s", s.Namespace, s.Name) + } + return fmt.Sprintf("%s#%s$%s", s.Namespace, s.Name, s.Member) +} + func stoid(s string) ShapeID { ns, n, _ := strings.Cut(s, "#") n, m, _ := strings.Cut(n, "$") @@ -53,36 +62,93 @@ func stoid(s string) ShapeID { // Generated clients use schemas at runtime to dynamically (de)serialize // request/responses. type Schema struct { - ID ShapeID - Type ShapeType - Members map[string]*Schema // member name -> schema - Traits map[string]Trait // trait ID -> trait + id ShapeID + typ ShapeType + members map[string]*Schema // member name -> schema + traits map[string]Trait // trait ID -> trait + + listMember *Schema + mapKey, mapValue *Schema } -// NewMember creates a member schema from a target schema, overriding traits. -// -// Traits provided for the member override any traits on the target if there -// is collision. -func NewMember(name string, target *Schema, traits ...Trait) *Schema { +// NewSchema creates a new Schema with the given shape ID and traits. +func NewSchema(id ShapeID, typ ShapeType, numMembers int, traits ...Trait) *Schema { + traitMap := make(map[string]Trait, len(traits)) + for _, t := range traits { + traitMap[t.TraitID()] = t + } + return &Schema{ + id: id, + typ: typ, + members: make(map[string]*Schema, numMembers), + traits: traitMap, + } +} + +// AddMember adds a member to the schema derived from the target, with +// optional trait overrides. The member schema is returned for caller +// reference. +func (s *Schema) AddMember(name string, target *Schema, traits ...Trait) *Schema { m := &Schema{ - ID: ShapeID{Member: name}, - Type: target.Type, - Members: target.Members, - Traits: maps.Clone(target.Traits), + id: ShapeID{Member: name}, + typ: target.typ, + members: target.members, + traits: maps.Clone(target.traits), } + if len(m.traits) == 0 && len(traits) != 0 { + m.traits = map[string]Trait{} + } for _, t := range traits { - m.Traits[t.TraitID()] = t + m.traits[t.TraitID()] = t } + s.members[name] = m + switch name { + case "member": + s.listMember = m + case "key": + s.mapKey = m + case "value": + s.mapValue = m + } return m } +// ListMember returns the "member" schema for list types. +func (s *Schema) ListMember() *Schema { + return s.listMember +} + +// MapKey returns the "key" schema for map types. +func (s *Schema) MapKey() *Schema { + return s.mapKey +} + +// MapValue returns the "value" schema for map types. +func (s *Schema) MapValue() *Schema { + return s.mapValue +} + +// MemberName returns the member component of the schema's shape ID. +func (s *Schema) MemberName() string { + return s.id.Member +} + +// Member returns the member schema for the given name, or nil. +func (s *Schema) Member(name string) *Schema { + return s.members[name] +} + // Trait returns the target trait on the schema if it exists. func SchemaTrait[T Trait](s *Schema) (T, bool) { var trait T - opaque, ok := s.Traits[trait.TraitID()] + if s == nil { + return trait, false + } + + opaque, ok := s.traits[trait.TraitID()] if !ok { return trait, false } diff --git a/serde.go b/serde.go new file mode 100644 index 000000000..cdd41e0b8 --- /dev/null +++ b/serde.go @@ -0,0 +1,220 @@ +package smithy + +import ( + "math/big" + "time" + + "github.com/aws/smithy-go/document" +) + +// Codec provides implementations of Serializer and ShapeDeserializer to be +// used by a Protocol. +type Codec interface { + Serializer() ShapeSerializer + Deserializer([]byte) ShapeDeserializer +} + +// ShapeSerializer implements the marshaling of an in-code representation of a +// shape to an unspecified data format, which is determined by the +// implementation. +type ShapeSerializer interface { + Bytes() []byte + + WriteInt8(*Schema, int8) + WriteInt16(*Schema, int16) + WriteInt32(*Schema, int32) + WriteInt64(*Schema, int64) + WriteInt8Ptr(*Schema, *int8) + WriteInt16Ptr(*Schema, *int16) + WriteInt32Ptr(*Schema, *int32) + WriteInt64Ptr(*Schema, *int64) + + WriteFloat32(*Schema, float32) + WriteFloat64(*Schema, float64) + WriteFloat32Ptr(*Schema, *float32) + WriteFloat64Ptr(*Schema, *float64) + + WriteBool(*Schema, bool) + WriteBoolPtr(*Schema, *bool) + + WriteString(*Schema, string) + WriteStringPtr(*Schema, *string) + + WriteBigInteger(*Schema, big.Int) + WriteBigDecimal(*Schema, big.Float) + WriteBlob(*Schema, []byte) + WriteTime(*Schema, time.Time) + WriteTimePtr(*Schema, *time.Time) + + WriteStruct(*Schema, Serializable) + + WriteUnion(schema, variant *Schema, v Serializable) + + WriteNil(*Schema) + + WriteList(*Schema) + CloseList() + + WriteMap(*Schema) + WriteKey(*Schema, string) + CloseMap() + + WriteDocument(*Schema, document.Value) +} + +// ShapeDeserializer implements the unmarshaling from some unspecified data +// format to an in-code representation of a shape, which is determined by the +// implementation. +type ShapeDeserializer interface { + ReadInt8(*Schema, *int8) error + ReadInt16(*Schema, *int16) error + ReadInt32(*Schema, *int32) error + ReadInt64(*Schema, *int64) error + + ReadInt8Ptr(*Schema, **int8) error + ReadInt16Ptr(*Schema, **int16) error + ReadInt32Ptr(*Schema, **int32) error + ReadInt64Ptr(*Schema, **int64) error + + ReadFloat32(*Schema, *float32) error + ReadFloat64(*Schema, *float64) error + + ReadFloat32Ptr(*Schema, **float32) error + ReadFloat64Ptr(*Schema, **float64) error + + ReadBool(*Schema, *bool) error + ReadBoolPtr(*Schema, **bool) error + + ReadString(*Schema, *string) error + ReadStringPtr(*Schema, **string) error + + ReadTime(*Schema, *time.Time) error + ReadTimePtr(*Schema, **time.Time) error + + ReadBlob(*Schema, *[]byte) error + + ReadList(*Schema) error + // returns true if there's another item in the list, false at the end and + // an error if a decode error is encountered. use other deserializer + // methods to read the expected type from the deserializer + ReadListItem(*Schema) (hasMoreElements bool, err error) + + ReadMap(*Schema) error + // the bool will be true if there's another key in the list and the string + // will have the value of that key, with any decode error in the error. use + // other deserializer methods to read the expected type. + ReadMapKey(*Schema) (key string, hasMoreElements bool, err error) + + ReadStruct(*Schema) error + // returns the member schema for the given struct, nil when there are no + // more members, with any decode error in the error. use other deserializer + // methods to read the expected type. + ReadStructMember() (*Schema, error) + + // returns the schema for the variant that the union is + ReadUnion(*Schema) (*Schema, error) + + // returns true if the next value is null (and consumes it) + ReadNil(*Schema) (bool, error) + + ReadDocument(*Schema, *document.Value) error +} + +// Serializable is an entity that can describe itself to a ShapeSerializer to +// be encoded to some format. +// +// Unlike the standard library marshaler interfaces, which idiomatically encode +// to []byte, the output format and data type here is not specified at all. +// This is because Smithy shapes need to encode to a variety of formats or data +// carriers. For example, HTTP-binding JSON protocols need to serialize some +// members to bytes (the HTTP request body) and others directly to fields on +// the HTTP request itself (e.g. headers). +type Serializable interface { + Serialize(ShapeSerializer) +} + +// Deserializable is an entity that can unmarshal itself from a +// ShapeDeserializer. +type Deserializable interface { + Deserialize(ShapeDeserializer) error +} + +// DeserializableError is implemented by modeled error types for a service. +type DeserializableError interface { + Deserializable + error +} + +// ReadStruct is a utility API for generated clients. +func ReadStruct(d ShapeDeserializer, schema *Schema, memberFn func(*Schema) error) error { + if err := d.ReadStruct(schema); err != nil { + return err + } + + for { + ms, err := d.ReadStructMember() + if ms == nil { + return nil + } + + if err != nil { + return err + } + + if err := memberFn(ms); err != nil { + return err + } + } +} + +// ReadList is a utility API for generated clients. +func ReadList(d ShapeDeserializer, schema *Schema, memberFn func() error) error { + if err := d.ReadList(schema); err != nil { + return err + } + + var memberSchema *Schema + if schema != nil { + memberSchema = schema.ListMember() + } + + for { + ok, err := d.ReadListItem(memberSchema) + if !ok { + return nil + } + if err != nil { + return err + } + + if err := memberFn(); err != nil { + return err + } + } +} + +// ReadMap is a utility API for generated clients. +func ReadMap(d ShapeDeserializer, schema *Schema, memberFn func(string) error) error { + if err := d.ReadMap(schema); err != nil { + return err + } + + var keySchema *Schema + if schema != nil { + keySchema = schema.MapKey() + } + + for { + k, ok, err := d.ReadMapKey(keySchema) + if !ok { + return nil + } + if err != nil { + return err + } + + if err := memberFn(k); err != nil { + return err + } + } +} diff --git a/transport/http/protocol.go b/transport/http/protocol.go new file mode 100644 index 000000000..4e6569d59 --- /dev/null +++ b/transport/http/protocol.go @@ -0,0 +1,19 @@ +package http + +import ( + "context" + + "github.com/aws/smithy-go" +) + +// ClientProtocol defines the interface through which client-side operation +// request/responses are (de)serialized across the wire. +// +// While a caller CAN define their own protocol, it is almost never necessary +// to do so. In practice, a generated client will utilize one of the predefined +// protocols implemented as part of the Smithy client runtime. +type ClientProtocol interface { + ID() string + SerializeRequest(context.Context, smithy.Serializable, *Request) error + DeserializeResponse(ctx context.Context, types *smithy.TypeRegistry, resp *Response, out smithy.Deserializable) error +} diff --git a/type_registry.go b/type_registry.go new file mode 100644 index 000000000..c3a9165b7 --- /dev/null +++ b/type_registry.go @@ -0,0 +1,61 @@ +package smithy + +import ( + "strings" +) + +// TypeRegistry creates an instance of a type based on its Smithy IDL shape ID. +// +// Generated clients have an exported package-level registry (named +// TypeRegistry) that holds all structure types for the service. +type TypeRegistry struct { + Entries map[string]*TypeRegistryEntry +} + +// RegistryEntry creates a type registry entry. +func RegistryEntry[T any](schema *Schema) *TypeRegistryEntry { + return &TypeRegistryEntry{ + Schema: schema, + New: func() any { + return new(T) + }, + } +} + +// DeserializableError provides an instance of a deserializable error structure +// for a given shape ID. +// +// The ID is given as a string here since this will be called in a context where +// a shape ID is a discriminator read in from some wire payload. +func (t *TypeRegistry) DeserializableError(id string) (DeserializableError, bool) { + return typeRegistryLookup[DeserializableError](t, id) +} + +// TypeRegistryEntry holds the schema and constructor for a registered shape. +type TypeRegistryEntry struct { + Schema *Schema + New func() any +} + +func (t *TypeRegistry) lookupShortName(id string) (*TypeRegistryEntry, bool) { + for key, e := range t.Entries { + if idx := strings.Index(key, "#"); idx != -1 && key[idx+1:] == id { + return e, true + } + } + return nil, false +} + +func typeRegistryLookup[T any](t *TypeRegistry, id string) (T, bool) { + entry, ok := t.Entries[id] + if !ok { + entry, ok = t.lookupShortName(id) + } + if !ok { + var v T + return v, false + } + + v, ok := entry.New().(T) + return v, ok +} From f23a348612e8c7d9c5292f96d4a914d44b49e02e Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Thu, 12 Mar 2026 12:07:07 -0400 Subject: [PATCH 05/38] serde2: implement aws-protocols/restjson1 (#636) * implement aws-protocols/restjson1 * Update aws-protocols/internal/httpbinding/serializer.go Co-authored-by: Luis Madrigal <599908+Madrigal@users.noreply.github.com> * remove old zv check * todo * switch * improve timestamp format switch * fixup ReadStringPtr * missed bad-state error in ReadBlob --------- Co-authored-by: Luis Madrigal <599908+Madrigal@users.noreply.github.com> --- aws-protocols/awsjson10/awsjson10.go | 52 +- .../internal/httpbinding/deserializer.go | 605 +++++++++++++++ .../internal/httpbinding/serializer.go | 734 ++++++++++++++++++ .../internal/httpbinding/timestamp.go | 44 ++ aws-protocols/internal/json/error.go | 54 ++ .../internal/json/shape_deserializer.go | 39 +- .../internal/json/shape_serializer.go | 127 ++- aws-protocols/restjson1/restjson1.go | 159 ++++ .../smithy/go/codegen/CodegenVisitor.java | 44 +- .../go/codegen/DefaultTraitGenerators.java | 7 + .../smithy/go/codegen/OperationGenerator.java | 18 +- .../smithy/go/codegen/SchemaGenerator.java | 21 +- .../smithy/go/codegen/ServiceGenerator.java | 24 +- .../go/codegen/SimpleTraitGenerator.java | 11 + .../smithy/go/codegen/SmithyGoDependency.java | 1 + .../smithy/go/codegen/StructureGenerator.java | 30 +- .../smithy/go/codegen/TypeRegistry.java | 2 +- .../smithy/go/codegen/UnionGenerator.java | 4 +- .../go/codegen/serde2/ListDeserializer.java | 36 +- .../go/codegen/serde2/ListSerializer.java | 10 +- .../go/codegen/serde2/MapDeserializer.java | 32 +- .../go/codegen/serde2/MapSerializer.java | 11 +- .../Serde2DeserializeResponseMiddleware.java | 3 +- .../Serde2SerializeRequestMiddleware.java | 5 +- .../codegen/serde2/StructureDeserializer.java | 9 +- .../codegen/serde2/StructureSerializer.java | 7 +- .../go/codegen/serde2/UnionDeserializer.java | 2 +- .../go/codegen/serde2/UnionSerializer.java | 2 +- .../smithy/go/codegen/util/ShapeUtil.java | 4 +- schema.go | 21 +- serde.go | 13 + traits/http.go | 18 + transport/http/protocol.go | 4 +- type_registry.go | 9 + 34 files changed, 2026 insertions(+), 136 deletions(-) create mode 100644 aws-protocols/internal/httpbinding/deserializer.go create mode 100644 aws-protocols/internal/httpbinding/serializer.go create mode 100644 aws-protocols/internal/httpbinding/timestamp.go create mode 100644 aws-protocols/internal/json/error.go create mode 100644 aws-protocols/restjson1/restjson1.go diff --git a/aws-protocols/awsjson10/awsjson10.go b/aws-protocols/awsjson10/awsjson10.go index 201fdae9f..d5152d1fd 100644 --- a/aws-protocols/awsjson10/awsjson10.go +++ b/aws-protocols/awsjson10/awsjson10.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "net/http" - "strings" "github.com/aws/smithy-go" awsjson "github.com/aws/smithy-go/aws-protocols/internal/json" @@ -36,6 +35,7 @@ func (*Protocol) ID() string { // SerializeRequest serializes a request for AWS Json 1.0. func (p *Protocol) SerializeRequest( ctx context.Context, + schema *smithy.Schema, in smithy.Serializable, req *smithyhttp.Request, ) error { @@ -61,6 +61,7 @@ func (p *Protocol) SerializeRequest( // DeserializeResponse deserializes a response for AWS Json 1.0. func (p *Protocol) DeserializeResponse( ctx context.Context, + schema *smithy.Schema, types *smithy.TypeRegistry, resp *smithyhttp.Response, out smithy.Deserializable, @@ -105,7 +106,7 @@ func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithy body := io.TeeReader(errorBody, ringBuffer) decoder := json.NewDecoder(body) decoder.UseNumber() - bodyInfo, err := getProtocolErrorInfo(decoder) + bodyInfo, err := awsjson.GetProtocolErrorInfo(decoder) if err != nil { var snapshot bytes.Buffer io.Copy(&snapshot, ringBuffer) @@ -117,14 +118,14 @@ func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithy } errorBody.Seek(0, io.SeekStart) - if typ, ok := resolveProtocolErrorType(headerCode, bodyInfo); ok { + if typ, ok := awsjson.ResolveProtocolErrorType(headerCode, bodyInfo); ok { errorCode = typ } if len(bodyInfo.Message) != 0 { errorMessage = bodyInfo.Message } - errorCode = sanitizeErrorCode(errorCode) + errorCode = awsjson.SanitizeErrorCode(errorCode) perr, ok := types.DeserializableError(errorCode) if !ok { @@ -146,46 +147,3 @@ func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithy return perr } - -type protocolErrorInfo struct { - Type string `json:"__type"` - Message string - - // nonstandard, but some AWS services do present the type here - Code any -} - -func getProtocolErrorInfo(decoder *json.Decoder) (protocolErrorInfo, error) { - var errInfo protocolErrorInfo - if err := decoder.Decode(&errInfo); err != nil { - if err == io.EOF { - return errInfo, nil - } - return errInfo, err - } - - return errInfo, nil -} - -func resolveProtocolErrorType(headerType string, bodyInfo protocolErrorInfo) (string, bool) { - if len(headerType) != 0 { - return headerType, true - } else if len(bodyInfo.Type) != 0 { - return bodyInfo.Type, true - } else if code, ok := bodyInfo.Code.(string); ok && len(code) != 0 { - return code, true - } - return "", false -} - -// sanitizeErrorCode strips namespace prefixes and URI suffixes from error -// codes received on the wire. -func sanitizeErrorCode(code string) string { - if idx := strings.Index(code, ":"); idx != -1 { - code = code[:idx] - } - if idx := strings.Index(code, "#"); idx != -1 { - code = code[idx+1:] - } - return code -} diff --git a/aws-protocols/internal/httpbinding/deserializer.go b/aws-protocols/internal/httpbinding/deserializer.go new file mode 100644 index 000000000..c3f2d37fd --- /dev/null +++ b/aws-protocols/internal/httpbinding/deserializer.go @@ -0,0 +1,605 @@ +package httpbinding + +import ( + "encoding/base64" + "fmt" + "net/http" + "strconv" + "strings" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/traits" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// ShapeDeserializer reads HTTP-bound output struct members from the response, +// delegates body members to the wrapped body deserializer. +type ShapeDeserializer struct { + response *http.Response + body smithy.ShapeDeserializer + payload []byte + + // ALL http binding traits are applied on the top-level output struct, for + // anything nested we are just delegating to the payload deserializer + depth int + topLevel *smithy.Schema + + // unlike an RPC-style protocol, members of the top-level output could be + // HTTP-bound, so we track that when ReadStruct is first called and "yield" + // them back to the caller through ReadStructMember + inBindings bool + bindings []*smithy.Schema + bindingIndex int + + inBody bool + hasPayload bool + + inHeaderList bool + headerListValues []string + headerListIdx int + + inPrefixMap bool + prefixValue string + prefixKeys []string + prefixKeyIdx int +} + +var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil) + +// ShapeDeserializerOptions configures ShapeDeserializer. +type ShapeDeserializerOptions struct{} + +// NewShapeDeserializer creates a ShapeDeserializer for the given HTTP +// response. +// +// The payload should be nil in streaming-blob response operations. +func NewShapeDeserializer(resp *http.Response, body smithy.ShapeDeserializer, payload []byte, opts ...func(*ShapeDeserializerOptions)) *ShapeDeserializer { + return &ShapeDeserializer{ + response: resp, + body: body, + payload: payload, + } +} + +// ReadStruct implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error { + d.depth++ + if d.depth > 1 { + return d.body.ReadStruct(s) + } + + d.topLevel = s + for _, member := range s.Members() { + if _, ok := smithy.SchemaTrait[*traits.HTTPPayload](member); ok { + d.hasPayload = true + } + if d.isBindingSet(member) { + d.bindings = append(d.bindings, member) + } + } + + return nil +} + +// ReadStructMember implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { + if d.depth > 1 { + ms, err := d.body.ReadStructMember() + if ms == nil { + d.depth-- + } + return ms, err + } + + if d.bindingIndex < len(d.bindings) { + member := d.bindings[d.bindingIndex] + d.bindingIndex++ + d.inBindings = true + return member, nil + } + d.inBindings = false + + if d.hasPayload { // i.e. no unbound members + d.depth-- + return nil, nil + } + + if !d.inBody { + d.inBody = true + if err := d.body.ReadStruct(d.topLevel); err != nil { + return nil, err + } + } + + ms, err := d.body.ReadStructMember() + if ms == nil { + d.depth-- + } + return ms, err +} + +// ReadString implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { + if d.inHeaderList { + *v = d.headerListValues[d.headerListIdx] + d.headerListIdx++ + return nil + } + if d.inBindings { + if _, ok := isHTTPHeader(s); ok { + hv, err := d.readHeaderString(s) + if err != nil { + return err + } + *v = hv + return nil + } + if _, ok := smithy.SchemaTrait[*traits.HTTPPayload](s); ok { + *v = string(d.payload) + return nil + } + } + if d.inPrefixMap { + key := d.prefixKeys[d.prefixKeyIdx-1] + *v = d.response.Header.Get(d.prefixValue + key) + return nil + } + return d.body.ReadString(s, v) +} + +// ReadStringPtr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadStringPtr(s *smithy.Schema, v **string) error { + if d.inBindings { + if _, ok := isHTTPHeader(s); ok { + hv, err := d.readHeaderString(s) + if err != nil { + return err + } + *v = &hv + return nil + } + } + var val string + if err := d.ReadString(s, &val); err != nil { + return err + } + if val != "" { + *v = &val + } + return nil +} + +// ReadBool implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { + if !d.inBindings { + return d.body.ReadBool(s, v) + } + + trait, _ := isHTTPHeader(s) + + var hv string + if d.inHeaderList { + hv = d.nextHeaderValue() + } else { + hv = d.response.Header.Get(trait.Name) + } + + n, err := strconv.ParseBool(hv) + if err != nil { + return err + } + + *v = n + return nil +} + +// ReadBoolPtr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadBoolPtr(s *smithy.Schema, v **bool) error { + var vv bool + if err := d.ReadBool(s, &vv); err != nil { + return err + } + + *v = &vv + return nil +} + +// ReadInt8 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt8(s *smithy.Schema, v *int8) error { + if !d.inBindings { + return d.body.ReadInt8(s, v) + } + return readHeaderInt[int8](d, s, v) +} + +// ReadInt8Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt8Ptr(s *smithy.Schema, v **int8) error { + var vv int8 + if err := d.ReadInt8(s, &vv); err != nil { + return err + } + + *v = &vv + return nil +} + +// ReadInt16 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt16(s *smithy.Schema, v *int16) error { + if !d.inBindings { + return d.body.ReadInt16(s, v) + } + return readHeaderInt[int16](d, s, v) +} + +// ReadInt16Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt16Ptr(s *smithy.Schema, v **int16) error { + var vv int16 + if err := d.ReadInt16(s, &vv); err != nil { + return err + } + + *v = &vv + return nil +} + +// ReadInt32 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt32(s *smithy.Schema, v *int32) error { + if !d.inBindings { + return d.body.ReadInt32(s, v) + } + + // https://smithy.io/2.0/spec/http-bindings.html#httpresponsecode-trait + // + // The httpResponseCode trait can be applied to structure members that + // target an integer within any structure that has no input trait applied. + if _, ok := smithy.SchemaTrait[*traits.HTTPResponseCode](s); ok { + *v = int32(d.response.StatusCode) + return nil + } + + return readHeaderInt[int32](d, s, v) +} + +// ReadInt32Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt32Ptr(s *smithy.Schema, v **int32) error { + var vv int32 + if err := d.ReadInt32(s, &vv); err != nil { + return err + } + + *v = &vv + return nil +} + +// ReadInt64 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { + if !d.inBindings { + return d.body.ReadInt64(s, v) + } + return readHeaderInt[int64](d, s, v) +} + +// ReadInt64Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt64Ptr(s *smithy.Schema, v **int64) error { + var vv int64 + if err := d.ReadInt64(s, &vv); err != nil { + return err + } + + *v = &vv + return nil +} + +// ReadFloat32 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error { + if !d.inBindings { + return d.body.ReadFloat32(s, v) + } + return readHeaderFloat[float32](d, s, v) +} + +// ReadFloat32Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat32Ptr(s *smithy.Schema, v **float32) error { + var vv float32 + if err := d.ReadFloat32(s, &vv); err != nil { + return err + } + + *v = &vv + return nil +} + +// ReadFloat64 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { + if !d.inBindings { + return d.body.ReadFloat64(s, v) + } + return readHeaderFloat[float64](d, s, v) +} + +// ReadFloat64Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat64Ptr(s *smithy.Schema, v **float64) error { + var vv float64 + if err := d.ReadFloat64(s, &vv); err != nil { + return err + } + + *v = &vv + return nil +} + +// ReadTime implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadTime(s *smithy.Schema, v *time.Time) error { + if d.inHeaderList { + return d.readHeaderListTime(func(t time.Time) { *v = t }) + } + if d.inBindings { + t, err := d.readHeaderTime(s) + if err != nil { + return err + } + *v = t + return nil + } + return d.body.ReadTime(s, v) +} + +// ReadTimePtr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadTimePtr(s *smithy.Schema, v **time.Time) error { + if d.inHeaderList { + var val time.Time + if err := d.ReadTime(s, &val); err != nil { + return err + } + *v = &val + return nil + } + if d.inBindings { + t, err := d.readHeaderTime(s) + if err != nil { + return err + } + *v = &t + return nil + } + return d.body.ReadTimePtr(s, v) +} + +func (d *ShapeDeserializer) readHeaderTime(s *smithy.Schema) (time.Time, error) { + h, _ := isHTTPHeader(s) + hv := d.response.Header.Get(h.Name) + t, err := parseTimestamp(s, "http-date", hv) + if err != nil { + return time.Time{}, err + } + return t, nil +} + +func (d *ShapeDeserializer) readHeaderListTime(assign func(time.Time)) error { + hv := d.headerListValues[d.headerListIdx] + d.headerListIdx++ + t, err := parseTimestamp(nil, "http-date", hv) + if err != nil { + return err + } + assign(t) + return nil +} + +// ReadBlob implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { + if !d.inBindings { + return d.body.ReadBlob(s, v) + } + + if _, ok := smithy.SchemaTrait[*traits.HTTPPayload](s); ok { + *v = d.payload + return nil + } + + h, ok := isHTTPHeader(s) + if !ok { + return fmt.Errorf("ReadBlob: unhandled http binding") + } + + hv := d.response.Header.Get(h.Name) + b, err := base64.StdEncoding.DecodeString(hv) + if err != nil { + return err + } + *v = b + return nil +} + +// ReadList implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { + if !d.inBindings { + return d.body.ReadList(s) + } + + h, ok := isHTTPHeader(s) + if !ok { + return fmt.Errorf("ReadList called outside of payload / http binding") + } + + d.inHeaderList = true + d.headerListIdx = 0 + if s.ListMember() != nil && s.ListMember().Type() == smithy.ShapeTypeTimestamp && timestampFormat(s.ListMember(), "http-date") == "http-date" { + vs, err := smithyhttp.SplitHTTPDateTimestampHeaderListValues(d.response.Header.Values(h.Name)) + if err != nil { + return err + } + + d.headerListValues = vs + } else { + vs, err := smithyhttp.SplitHeaderListValues(d.response.Header.Values(h.Name)) + if err != nil { + return err + } + + d.headerListValues = vs + } + return nil +} + +// ReadListItem implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadListItem(s *smithy.Schema) (bool, error) { + if !d.inHeaderList { + return d.body.ReadListItem(s) + } + + if d.headerListIdx >= len(d.headerListValues) { + d.inHeaderList = false + return false, nil + } + return true, nil +} + +// ReadMap implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadMap(s *smithy.Schema) error { + if !d.inBindings { + return d.body.ReadMap(s) + } + + ph, ok := smithy.SchemaTrait[*traits.HTTPPrefixHeaders](s) + if !ok { + return fmt.Errorf("ReadMap called outside of payload / http binding") + } + + d.inPrefixMap = true + d.prefixValue = ph.Prefix + d.prefixKeyIdx = 0 + + canon := http.CanonicalHeaderKey(ph.Prefix) + for name := range d.response.Header { + if len(name) > len(canon) && strings.EqualFold(name[:len(canon)], canon) { + d.prefixKeys = append(d.prefixKeys, strings.ToLower(name[len(canon):])) + } + } + + return nil +} + +// ReadMapKey implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadMapKey(s *smithy.Schema) (string, bool, error) { + if !d.inPrefixMap { + return d.body.ReadMapKey(s) + } + + if d.prefixKeyIdx >= len(d.prefixKeys) { + d.inPrefixMap = false + return "", false, nil + } + key := d.prefixKeys[d.prefixKeyIdx] + d.prefixKeyIdx++ + return key, true, nil +} + +// ReadNil implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadNil(s *smithy.Schema) (bool, error) { + return d.body.ReadNil(s) +} + +// ReadDocument implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadDocument(s *smithy.Schema, v *document.Value) error { + return d.body.ReadDocument(s, v) +} + +// ReadUnion implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) { + return d.body.ReadUnion(s) +} + +func (d *ShapeDeserializer) isBindingSet(schema *smithy.Schema) bool { + if trait, ok := isHTTPHeader(schema); ok { + return len(d.response.Header.Values(trait.Name)) > 0 + } + + if trait, ok := isHTTPPrefixHeaders(schema); ok { + canon := http.CanonicalHeaderKey(trait.Prefix) + for name := range d.response.Header { + if len(name) > len(canon) && strings.EqualFold(name[:len(canon)], canon) { + return true + } + } + return false + } + + if _, ok := smithy.SchemaTrait[*traits.HTTPResponseCode](schema); ok { + return true + } + + if _, ok := smithy.SchemaTrait[*traits.HTTPPayload](schema); ok { + return len(d.payload) > 0 + } + + return false +} + +func (d *ShapeDeserializer) readHeaderString(s *smithy.Schema) (string, error) { + trait, _ := isHTTPHeader(s) + + hv := d.response.Header.Get(trait.Name) + if _, ok := smithy.SchemaTrait[*traits.MediaType](s); ok { + b, err := base64.StdEncoding.DecodeString(hv) + if err != nil { + return "", err + } + hv = string(b) + } + return hv, nil +} + +func (d *ShapeDeserializer) nextHeaderValue() string { + v := d.headerListValues[d.headerListIdx] + d.headerListIdx++ + return v +} + +type intn interface { + int8 | int16 | int32 | int64 +} + +func readHeaderInt[T intn](d *ShapeDeserializer, s *smithy.Schema, v *T) error { + trait, _ := isHTTPHeader(s) + + var hv string + if d.inHeaderList { + hv = d.nextHeaderValue() + } else { + hv = d.response.Header.Get(trait.Name) + } + + n, err := strconv.ParseInt(hv, 10, 64) + if err != nil { + return err + } + + *v = T(n) + return nil +} + +type floatn interface { + float32 | float64 +} + +func readHeaderFloat[T floatn](d *ShapeDeserializer, s *smithy.Schema, v *T) error { + trait, _ := isHTTPHeader(s) + + var hv string + if d.inHeaderList { + hv = d.nextHeaderValue() + } else { + hv = d.response.Header.Get(trait.Name) + } + + n, err := strconv.ParseFloat(hv, 64) + if err != nil { + return err + } + + *v = T(n) + return nil +} diff --git a/aws-protocols/internal/httpbinding/serializer.go b/aws-protocols/internal/httpbinding/serializer.go new file mode 100644 index 000000000..7886b4dcb --- /dev/null +++ b/aws-protocols/internal/httpbinding/serializer.go @@ -0,0 +1,734 @@ +package httpbinding + +import ( + "bytes" + "encoding/base64" + "fmt" + "io" + "math/big" + "net/http" + "strconv" + "strings" + "time" + + "github.com/aws/smithy-go" + awsjson "github.com/aws/smithy-go/aws-protocols/internal/json" + "github.com/aws/smithy-go/document" + httpbinding "github.com/aws/smithy-go/encoding/httpbinding" + "github.com/aws/smithy-go/traits" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// ShapeSerializer routes top-level input struct members to their HTTP binding +// locations. Members without HTTP binding traits delegate to an inner +// serializer for whatever protocol is being used. +// +// ShapeSerializer adds some API surface on top of the normal +// smithy.ShapeSerializer. Specifically it adds [GetRequestBody] for handing +// REST-protocol payloads, since the actual source of the payload is going to +// vary on a per-operation basis and isn't known until the input's Serialize is +// called. The caller (so, the protocol implementation) should set the HTTP +// request body according to the return of GetRequestBody. +type ShapeSerializer struct { + request *smithyhttp.Request + encoder *httpbinding.Encoder + input smithy.ShapeSerializer + options ShapeSerializerOptions + + // set when an input member is bound via @httpPayload + httpPayload []byte + httpPayloadContentType string + + // set when an input blob is bound via @httpPayload + @streaming + streamingContentType string + + mapMode mapBindingMode + mapPrefix string + currentKey string + + listMode listBindingMode + listName string + listHasItems bool + + noBody bool + hasStructPayload bool +} + +// ShapeSerializerOptions configures a ShapeSerializer. +type ShapeSerializerOptions struct { + WriteZeroValues bool +} + +// NewShapeSerializer creates a ShapeSerializer for the given operation schema +// and request. It handles the initial setup from use of an +// httpbinding.Encoder. +func NewShapeSerializer(op *smithy.Schema, req *smithyhttp.Request, in smithy.ShapeSerializer, opts ...func(*ShapeSerializerOptions)) (*ShapeSerializer, error) { + httpTrait, ok := smithy.SchemaTrait[*traits.HTTP](op) + if !ok { + return nil, fmt.Errorf("no @http trait on op schema") + } + + req.Method = httpTrait.Method + path, query := httpbinding.SplitURI(httpTrait.URI) + enc, err := httpbinding.NewEncoder(path, query, req.Header) + if err != nil { + return nil, fmt.Errorf("new encoder: %w", err) + } + + return &ShapeSerializer{ + request: req, + encoder: enc, + input: in, + }, nil +} + +// GetRequestBody resolves the serialized body after Serialize has been called. +// It encodes the httpbinding values into the request and returns the stream +// and content type for the body. +// +// The body is resolved in the following priority: +// 1. Streaming payload (input implements StreamingInput with non-nil stream) +// 2. Raw payload bytes (blob/string member with @httpPayload) +// 3. Serialized protocol body (e.g. JSON) +// 4. Empty struct payload (struct member with @httpPayload, sends "{}") +// +// Build encodes HTTP binding values into the request and sets the request +// body. The defaultContentType is used for the protocol body (e.g. +// "application/json") when no explicit payload is present. +func (s *ShapeSerializer) Build(in smithy.Serializable, defaultContentType string) error { + req := s.request + + built, err := s.encoder.Encode(req.Request) + if err != nil { + return fmt.Errorf("encode httpbinding: %w", err) + } + req.Request = built + + // (1) streaming payload + if si, ok := in.(smithy.StreamingInput); ok && si.GetPayloadStream() != nil { + contentType := s.streamingContentType + if contentType == "" { + contentType = "application/octet-stream" + } + return s.setBody(si.GetPayloadStream(), contentType) + } + + var payload []byte + var contentType string + + // (2) explicit @httpPayload (blob/string) + if s.httpPayload != nil { + payload = s.httpPayload + contentType = s.httpPayloadContentType + } else { // (3) protocol body + payload = s.input.Bytes() + contentType = defaultContentType + } + + // (4) empty struct @httpPayload + if len(payload) == 0 && s.hasStructPayload { + payload = []byte("{}") + contentType = defaultContentType + } + + if len(payload) == 0 { + return nil + } + return s.setBody(bytes.NewReader(payload), contentType) +} + +func (s *ShapeSerializer) setBody(body io.Reader, contentType string) error { + if s.request.Header.Get("Content-Type") == "" { + s.request.Header.Set("Content-Type", contentType) + } + sreq, err := s.request.SetStream(body) + if err != nil { + return fmt.Errorf("set stream: %w", err) + } + *s.request = *sreq + return nil +} + +// withWriteZero allows temporarily setting to true the ShapeSerializer `options.WriteZeroValues` +// This is useful for writing pointer values that have an empty value, like `&""`, which would be skipped +// otherwise if `options.WriteZeroValues` would be set to false +func (s *ShapeSerializer) withWriteZero(fn func()) { + prev := s.options.WriteZeroValues + s.options.WriteZeroValues = true + fn() + s.options.WriteZeroValues = prev +} + +type mapBindingMode int + +const ( + mapModeNone mapBindingMode = iota + mapModePrefixHeaders + mapModeQueryParams +) + +type listBindingMode int + +const ( + listModeNone listBindingMode = iota + listModeHeader + listModeQuery +) + +type bind int + +const ( + bindBody bind = iota + bindHeader + bindHeaderList + bindQuery + bindQueryList + bindLabel +) + +func (s *ShapeSerializer) resolveBinding(schema *smithy.Schema) (bind, string) { + if s.listMode == listModeHeader { + return bindHeaderList, s.listName + } + if s.listMode == listModeQuery { + return bindQueryList, s.listName + } + if h, ok := isHTTPHeader(schema); ok { + return bindHeader, h.Name + } + if isHTTPLabel(schema) { + return bindLabel, schema.MemberName() + } + if q, ok := isHTTPQuery(schema); ok { + return bindQuery, q.Name + } + return bindBody, "" +} + +var _ smithy.ShapeSerializer = (*ShapeSerializer)(nil) + +// Bytes returns the serialized body bytes. +func (s *ShapeSerializer) Bytes() []byte { + return s.input.Bytes() +} + +func isHTTPHeader(schema *smithy.Schema) (*traits.HTTPHeader, bool) { + h, ok := smithy.SchemaTrait[*traits.HTTPHeader](schema) + if ok { + h.Name = http.CanonicalHeaderKey(h.Name) + } + return h, ok +} + +func isHTTPLabel(schema *smithy.Schema) bool { + _, ok := smithy.SchemaTrait[*traits.HTTPLabel](schema) + return ok +} + +func isHTTPQuery(schema *smithy.Schema) (*traits.HTTPQuery, bool) { + return smithy.SchemaTrait[*traits.HTTPQuery](schema) +} + +func isHTTPPayload(schema *smithy.Schema) bool { + _, ok := smithy.SchemaTrait[*traits.HTTPPayload](schema) + return ok +} + +func isHTTPPrefixHeaders(schema *smithy.Schema) (*traits.HTTPPrefixHeaders, bool) { + ph, ok := smithy.SchemaTrait[*traits.HTTPPrefixHeaders](schema) + return ph, ok +} + +func isHTTPQueryParams(schema *smithy.Schema) bool { + _, ok := smithy.SchemaTrait[*traits.HTTPQueryParams](schema) + return ok +} + +func hasBodyMembers(schema *smithy.Schema) bool { + for _, member := range schema.Members() { + if !isHTTPBound(member) { + return true + } + } + return false +} + +func isHTTPBound(schema *smithy.Schema) bool { + if _, ok := isHTTPHeader(schema); ok { + return true + } + if _, ok := isHTTPPrefixHeaders(schema); ok { + return true + } + if isHTTPLabel(schema) { + return true + } + if _, ok := isHTTPQuery(schema); ok { + return true + } + if isHTTPQueryParams(schema) { + return true + } + if isHTTPPayload(schema) { + return true + } + return false +} + +// WriteString implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { + switch s.mapMode { + case mapModePrefixHeaders: + s.encoder.SetHeader(http.CanonicalHeaderKey(s.mapPrefix + s.currentKey)).String(v) + return + case mapModeQueryParams: + s.encoder.AddQuery(s.currentKey).String(v) + return + } + + bt, bn := s.resolveBinding(schema) + switch bt { + case bindHeaderList: + escaped := v + if strings.ContainsAny(v, ",\"") { + escaped = strconv.Quote(v) + } + s.encoder.AddHeader(bn).String(escaped) + s.listHasItems = true + case bindQueryList: + s.encoder.AddQuery(bn).String(v) + case bindHeader: + if _, ok := smithy.SchemaTrait[*traits.MediaType](schema); ok { + s.encoder.SetHeader(bn).String(base64.StdEncoding.EncodeToString([]byte(v))) + return + } + s.encoder.SetHeader(bn).String(v) + case bindLabel: + s.encoder.SetURI(bn).String(v) + case bindQuery: + s.encoder.SetQuery(bn).String(v) + default: + if isHTTPPayload(schema) { + s.httpPayload = []byte(v) + s.httpPayloadContentType = "text/plain" + return + } + if s.options.WriteZeroValues { + s.input.WriteStringPtr(schema, &v) + } else { + s.input.WriteString(schema, v) + } + } +} + +// WriteStringPtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStringPtr(schema *smithy.Schema, v *string) { + if v != nil { + s.withWriteZero(func() { s.WriteString(schema, *v) }) + } +} + +// WriteBool implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { + bt, bn := s.resolveBinding(schema) + switch bt { + case bindHeaderList: + s.encoder.AddHeader(bn).Boolean(v) + s.listHasItems = true + case bindQueryList: + s.encoder.AddQuery(bn).Boolean(v) + case bindHeader: + s.encoder.SetHeader(bn).Boolean(v) + case bindLabel: + s.encoder.SetURI(bn).Boolean(v) + case bindQuery: + s.encoder.SetQuery(bn).Boolean(v) + default: + if s.options.WriteZeroValues { + s.input.WriteBoolPtr(schema, &v) + } else { + s.input.WriteBool(schema, v) + } + } +} + +// WriteBoolPtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBoolPtr(schema *smithy.Schema, v *bool) { + if v != nil { + s.withWriteZero(func() { s.WriteBool(schema, *v) }) + } +} + +// WriteInt8 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { + bt, bn := s.resolveBinding(schema) + switch bt { + case bindHeaderList: + s.encoder.AddHeader(bn).Byte(v) + s.listHasItems = true + case bindQueryList: + s.encoder.AddQuery(bn).Byte(v) + case bindHeader: + s.encoder.SetHeader(bn).Byte(v) + case bindLabel: + s.encoder.SetURI(bn).Byte(v) + case bindQuery: + s.encoder.SetQuery(bn).Byte(v) + default: + if s.options.WriteZeroValues { + s.input.WriteInt8Ptr(schema, &v) + } else { + s.input.WriteInt8(schema, v) + } + } +} + +// WriteInt8Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8Ptr(schema *smithy.Schema, v *int8) { + if v != nil { + s.withWriteZero(func() { s.WriteInt8(schema, *v) }) + } +} + +// WriteInt16 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { + bt, bn := s.resolveBinding(schema) + switch bt { + case bindHeaderList: + s.encoder.AddHeader(bn).Short(v) + s.listHasItems = true + case bindQueryList: + s.encoder.AddQuery(bn).Short(v) + case bindHeader: + s.encoder.SetHeader(bn).Short(v) + case bindLabel: + s.encoder.SetURI(bn).Short(v) + case bindQuery: + s.encoder.SetQuery(bn).Short(v) + default: + if s.options.WriteZeroValues { + s.input.WriteInt16Ptr(schema, &v) + } else { + s.input.WriteInt16(schema, v) + } + } +} + +// WriteInt16Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16Ptr(schema *smithy.Schema, v *int16) { + if v != nil { + s.withWriteZero(func() { s.WriteInt16(schema, *v) }) + } +} + +// WriteInt32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { + bt, bn := s.resolveBinding(schema) + switch bt { + case bindHeaderList: + s.encoder.AddHeader(bn).Integer(v) + s.listHasItems = true + case bindQueryList: + s.encoder.AddQuery(bn).Integer(v) + case bindHeader: + s.encoder.SetHeader(bn).Integer(v) + case bindLabel: + s.encoder.SetURI(bn).Integer(v) + case bindQuery: + s.encoder.SetQuery(bn).Integer(v) + default: + if s.options.WriteZeroValues { + s.input.WriteInt32Ptr(schema, &v) + } else { + s.input.WriteInt32(schema, v) + } + } +} + +// WriteInt32Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32Ptr(schema *smithy.Schema, v *int32) { + if v != nil { + s.withWriteZero(func() { s.WriteInt32(schema, *v) }) + } +} + +// WriteInt64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { + bt, bn := s.resolveBinding(schema) + switch bt { + case bindHeaderList: + s.encoder.AddHeader(bn).Long(v) + s.listHasItems = true + case bindQueryList: + s.encoder.AddQuery(bn).Long(v) + case bindHeader: + s.encoder.SetHeader(bn).Long(v) + case bindLabel: + s.encoder.SetURI(bn).Long(v) + case bindQuery: + s.encoder.SetQuery(bn).Long(v) + default: + if s.options.WriteZeroValues { + s.input.WriteInt64Ptr(schema, &v) + } else { + s.input.WriteInt64(schema, v) + } + } +} + +// WriteInt64Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64Ptr(schema *smithy.Schema, v *int64) { + if v != nil { + s.withWriteZero(func() { s.WriteInt64(schema, *v) }) + } +} + +// WriteFloat32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { + bt, bn := s.resolveBinding(schema) + switch bt { + case bindHeaderList: + s.encoder.AddHeader(bn).Float(v) + s.listHasItems = true + case bindQueryList: + s.encoder.AddQuery(bn).Float(v) + case bindHeader: + s.encoder.SetHeader(bn).Float(v) + case bindLabel: + s.encoder.SetURI(bn).Float(v) + case bindQuery: + s.encoder.SetQuery(bn).Float(v) + default: + if s.options.WriteZeroValues { + s.input.WriteFloat32Ptr(schema, &v) + } else { + s.input.WriteFloat32(schema, v) + } + } +} + +// WriteFloat32Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32Ptr(schema *smithy.Schema, v *float32) { + if v != nil { + s.withWriteZero(func() { s.WriteFloat32(schema, *v) }) + } +} + +// WriteFloat64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { + bt, bn := s.resolveBinding(schema) + switch bt { + case bindHeaderList: + s.encoder.AddHeader(bn).Double(v) + s.listHasItems = true + case bindQueryList: + s.encoder.AddQuery(bn).Double(v) + case bindHeader: + s.encoder.SetHeader(bn).Double(v) + case bindLabel: + s.encoder.SetURI(bn).Double(v) + case bindQuery: + s.encoder.SetQuery(bn).Double(v) + default: + if s.options.WriteZeroValues { + s.input.WriteFloat64Ptr(schema, &v) + } else { + s.input.WriteFloat64(schema, v) + } + } +} + +// WriteFloat64Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64Ptr(schema *smithy.Schema, v *float64) { + if v != nil { + s.withWriteZero(func() { s.WriteFloat64(schema, *v) }) + } +} + +// WriteBlob implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { + if isHTTPPayload(schema) { + s.httpPayload = v + if mt, ok := smithy.SchemaTrait[*traits.MediaType](schema); ok { + s.httpPayloadContentType = mt.Type + } else { + s.httpPayloadContentType = "application/octet-stream" + } + return + } + if h, ok := isHTTPHeader(schema); ok { + s.encoder.SetHeader(h.Name).Blob(v) + return + } + s.input.WriteBlob(schema, v) +} + +// WriteTime implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { + bt, bn := s.resolveBinding(schema) + switch bt { + case bindHeaderList: + s.encoder.AddHeader(bn).String(formatTimestamp(schema, "http-date", v)) + s.listHasItems = true + case bindQueryList: + s.encoder.AddQuery(bn).String(formatTimestamp(schema, "date-time", v)) + case bindHeader: + s.encoder.SetHeader(bn).String(formatTimestamp(schema, "http-date", v)) + case bindLabel: + s.encoder.SetURI(bn).String(formatTimestamp(schema, "date-time", v)) + case bindQuery: + s.encoder.SetQuery(bn).String(formatTimestamp(schema, "date-time", v)) + default: + s.input.WriteTime(schema, v) + } +} + +// WriteTimePtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { + if v != nil { + s.withWriteZero(func() { s.WriteTime(schema, *v) }) + } +} + +// WriteList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { + if s.mapMode == mapModeQueryParams { + s.listMode = listModeQuery + s.listName = s.currentKey + return + } + if h, ok := isHTTPHeader(schema); ok { + s.listMode = listModeHeader + s.listName = h.Name + s.listHasItems = false + return + } + if q, ok := isHTTPQuery(schema); ok { + s.listMode = listModeQuery + s.listName = q.Name + s.listHasItems = false + return + } + s.input.WriteList(schema) +} + +// CloseList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseList() { + if s.listMode != listModeNone { + if !s.listHasItems && s.listMode == listModeHeader { + s.encoder.SetHeader(s.listName).String("") + } + s.listMode = listModeNone + s.listName = "" + s.listHasItems = false + return + } + s.input.CloseList() +} + +// WriteMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { + if ph, ok := isHTTPPrefixHeaders(schema); ok { + s.mapMode = mapModePrefixHeaders + s.mapPrefix = ph.Prefix + return + } + + if isHTTPQueryParams(schema) { + s.mapMode = mapModeQueryParams + return + } + + // TODO(serde2): there is some weirdness going on here because the + // generated Serialize() methods use WriteMap to open their structure + // declaration. So we need to check if that's what's going on here. + // + // ShapeSerializer.WriteStruct is specifically for basically just + // delegating to the provided Serializable. We're probably going to have to + // split out to Serialize + SerializeFields so we can differentiate. It + // looks like smithy-java did something like this. + for _, m := range schema.Members() { + if !isHTTPPayload(m) { + continue + } + + if _, ok := smithy.SchemaTrait[*traits.Streaming](m); ok { + s.streamingContentType = "application/octet-stream" + if mt, ok := smithy.SchemaTrait[*traits.MediaType](m); ok { + s.streamingContentType = mt.Type + } + } else if m.Type() == smithy.ShapeTypeStructure { + s.hasStructPayload = true + } + } + + if !hasBodyMembers(schema) { + s.noBody = true + return + } + + s.input.WriteMap(schema) +} + +// WriteKey implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteKey(schema *smithy.Schema, key string) { + switch s.mapMode { + case mapModePrefixHeaders, mapModeQueryParams: + s.currentKey = key + default: + s.input.WriteKey(schema, key) + } +} + +// CloseMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseMap() { + if s.mapMode != mapModeNone { + s.mapMode = mapModeNone + s.mapPrefix = "" + s.currentKey = "" + return + } + if s.noBody { + s.noBody = false + return + } + s.input.CloseMap() +} + +// WriteStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema, v smithy.Serializable) { + s.input.WriteStruct(schema, v) +} + +// WriteUnion implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema, v smithy.Serializable) { + s.input.WriteUnion(schema, variant, v) +} + +// WriteNil implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteNil(schema *smithy.Schema) { + s.input.WriteNil(schema) +} + +// WriteBigInteger implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBigInteger(schema *smithy.Schema, v big.Int) { + s.input.WriteBigInteger(schema, v) +} + +// WriteBigDecimal implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBigDecimal(schema *smithy.Schema, v big.Float) { + s.input.WriteBigDecimal(schema, v) +} + +// WriteDocument implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteDocument(schema *smithy.Schema, v document.Value) { + if isHTTPPayload(schema) { + // httpPayload document: serialize to raw bytes for the body. + doc := awsjson.NewShapeSerializer() + doc.WriteDocument(schema, v) + s.httpPayload = doc.Bytes() + s.httpPayloadContentType = "application/json" + return + } + s.input.WriteDocument(schema, v) +} diff --git a/aws-protocols/internal/httpbinding/timestamp.go b/aws-protocols/internal/httpbinding/timestamp.go new file mode 100644 index 000000000..335ac6c52 --- /dev/null +++ b/aws-protocols/internal/httpbinding/timestamp.go @@ -0,0 +1,44 @@ +package httpbinding + +import ( + "fmt" + "strconv" + "time" + + "github.com/aws/smithy-go" + smithytime "github.com/aws/smithy-go/time" + "github.com/aws/smithy-go/traits" +) + +func timestampFormat(schema *smithy.Schema, fallback string) string { + if tf, ok := smithy.SchemaTrait[*traits.TimestampFormat](schema); ok { + return tf.Format + } + return fallback +} + +func formatTimestamp(schema *smithy.Schema, fallback string, v time.Time) string { + switch timestampFormat(schema, fallback) { + case "http-date": + return smithytime.FormatHTTPDate(v) + case "date-time": + return smithytime.FormatDateTime(v) + default: // "epoch-seconds" + return strconv.FormatFloat(smithytime.FormatEpochSeconds(v), 'f', -1, 64) + } +} + +func parseTimestamp(schema *smithy.Schema, fallback, s string) (time.Time, error) { + switch timestampFormat(schema, fallback) { + case "http-date": + return smithytime.ParseHTTPDate(s) + case "date-time": + return smithytime.ParseDateTime(s) + default: // "epoch-seconds" + v, err := strconv.ParseFloat(s, 64) + if err != nil { + return time.Time{}, fmt.Errorf("parse epoch-seconds %q: %w", s, err) + } + return smithytime.ParseEpochSeconds(v), nil + } +} diff --git a/aws-protocols/internal/json/error.go b/aws-protocols/internal/json/error.go new file mode 100644 index 000000000..49a530dcc --- /dev/null +++ b/aws-protocols/internal/json/error.go @@ -0,0 +1,54 @@ +package json + +import ( + "encoding/json" + "io" + "strings" +) + +// ProtocolErrorInfo holds the error type and message decoded from a JSON +// error response body. +type ProtocolErrorInfo struct { + Type string `json:"__type"` + Message string + + // nonstandard, but some AWS services do present the type here + Code any +} + +// GetProtocolErrorInfo decodes error type/message from a JSON response body. +func GetProtocolErrorInfo(decoder *json.Decoder) (ProtocolErrorInfo, error) { + var errInfo ProtocolErrorInfo + if err := decoder.Decode(&errInfo); err != nil { + if err == io.EOF { + return errInfo, nil + } + return errInfo, err + } + return errInfo, nil +} + +// ResolveProtocolErrorType resolves the error type from the header value and +// body info, returning the type and whether one was found. +func ResolveProtocolErrorType(headerType string, bodyInfo ProtocolErrorInfo) (string, bool) { + if len(headerType) != 0 { + return headerType, true + } else if len(bodyInfo.Type) != 0 { + return bodyInfo.Type, true + } else if code, ok := bodyInfo.Code.(string); ok && len(code) != 0 { + return code, true + } + return "", false +} + +// SanitizeErrorCode strips namespace prefixes and URI suffixes from error +// codes received on the wire. +func SanitizeErrorCode(code string) string { + if idx := strings.Index(code, ":"); idx != -1 { + code = code[:idx] + } + if idx := strings.Index(code, "#"); idx != -1 { + code = code[idx+1:] + } + return code +} diff --git a/aws-protocols/internal/json/shape_deserializer.go b/aws-protocols/internal/json/shape_deserializer.go index 4095a336c..3a5043168 100644 --- a/aws-protocols/internal/json/shape_deserializer.go +++ b/aws-protocols/internal/json/shape_deserializer.go @@ -19,6 +19,7 @@ import ( type ShapeDeserializer struct { dec *json.Decoder head stack + opts ShapeDeserializerOptions // json.Decoder does not have a Peek() but we need to be able to // "lookahead" for conditionally pulling a null token out in ReadNil. @@ -26,11 +27,24 @@ type ShapeDeserializer struct { hasPeek bool } +// ShapeDeserializerOptions configures ShapeDeserializer. +type ShapeDeserializerOptions struct { + // UseJSONName controls whether the @jsonName trait is used to + // match JSON object keys to struct members. If false (the default), + // only the member name is used. Protocols like restJson1 set this + // to true, while RPC protocols like awsJson1_0 leave it false. + UseJSONName bool +} + // NewShapeDeserializer creates a new ShapeDeserializer. -func NewShapeDeserializer(p []byte) *ShapeDeserializer { +func NewShapeDeserializer(p []byte, opts ...func(*ShapeDeserializerOptions)) *ShapeDeserializer { + o := ShapeDeserializerOptions{} + for _, fn := range opts { + fn(&o) + } dec := json.NewDecoder(bytes.NewReader(p)) dec.UseNumber() - return &ShapeDeserializer{dec: dec} + return &ShapeDeserializer{dec: dec, opts: o} } var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil) @@ -251,6 +265,9 @@ func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { if err != nil { return err } + if tok == nil { + return nil + } str, ok := tok.(string) if !ok { @@ -437,8 +454,15 @@ func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { } member := schema.Member(key) + if member == nil && d.opts.UseJSONName { + for _, m := range schema.Members() { + if jn, ok := smithy.SchemaTrait[*traits.JSONName](m); ok && jn.Name == key { + member = m + break + } + } + } if member == nil { - // TODO smithy.api#jsonName if err := d.skip(); err != nil { return nil, err } @@ -462,6 +486,15 @@ func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) } member := s.Member(key) + if member == nil && d.opts.UseJSONName { + // Try matching by jsonName trait + for _, m := range s.Members() { + if jn, ok := smithy.SchemaTrait[*traits.JSONName](m); ok && jn.Name == key { + member = m + break + } + } + } if member == nil { continue } diff --git a/aws-protocols/internal/json/shape_serializer.go b/aws-protocols/internal/json/shape_serializer.go index 9ae53f414..072bfff13 100644 --- a/aws-protocols/internal/json/shape_serializer.go +++ b/aws-protocols/internal/json/shape_serializer.go @@ -28,6 +28,15 @@ type ShapeSerializerOptions struct { // // Pointer write methods are NOT affected by this option. WriteZeroValues bool + + // Controls whether the @jsonName trait is used to + // determine JSON object keys. If false (the default), the member + // name is used as-is. + // + // How this is set in practice depends on the protocol. RPC-style protocols + // like awsjson10 ignore @jsonName, REST-style protocols like restjson1 + // respect it. + UseJSONName bool } var _ smithy.ShapeSerializer = (*ShapeSerializer)(nil) @@ -112,16 +121,31 @@ func (s *ShapeSerializer) withWriteZero(fn func()) { s.opts.WriteZeroValues = prev } +func (s *ShapeSerializer) skipZeroValue() bool { + if s.opts.WriteZeroValues { + return false + } + switch s.head.Top().(type) { + case *smithyjson.Array, smithyjson.Value: + return false + default: + return true + } +} + // WriteBool implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { - if !s.opts.WriteZeroValues && !v { + if !v && s.skipZeroValue() { return } switch enc := s.head.Top().(type) { case *smithyjson.Object: - enc.Key(schema.MemberName()).Boolean(v) + enc.Key(s.jsonMemberName(schema)).Boolean(v) case *smithyjson.Array: enc.Value().Boolean(v) + case smithyjson.Value: + enc.Boolean(v) + s.head.Pop() default: s.root.Boolean(v) } @@ -129,14 +153,17 @@ func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { // WriteInt8 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { - if !s.opts.WriteZeroValues && v == 0 { + if v == 0 && s.skipZeroValue() { return } switch enc := s.head.Top().(type) { case *smithyjson.Object: - enc.Key(schema.MemberName()).Byte(v) + enc.Key(s.jsonMemberName(schema)).Byte(v) case *smithyjson.Array: enc.Value().Byte(v) + case smithyjson.Value: + enc.Byte(v) + s.head.Pop() default: s.root.Byte(v) } @@ -144,14 +171,17 @@ func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { // WriteInt16 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { - if !s.opts.WriteZeroValues && v == 0 { + if v == 0 && s.skipZeroValue() { return } switch enc := s.head.Top().(type) { case *smithyjson.Object: - enc.Key(schema.MemberName()).Short(v) + enc.Key(s.jsonMemberName(schema)).Short(v) case *smithyjson.Array: enc.Value().Short(v) + case smithyjson.Value: + enc.Short(v) + s.head.Pop() default: s.root.Short(v) } @@ -159,12 +189,12 @@ func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { // WriteInt32 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { - if !s.opts.WriteZeroValues && v == 0 { + if v == 0 && s.skipZeroValue() { return } switch enc := s.head.Top().(type) { case *smithyjson.Object: - enc.Key(schema.MemberName()).Integer(v) + enc.Key(s.jsonMemberName(schema)).Integer(v) case *smithyjson.Array: enc.Value().Integer(v) case smithyjson.Value: @@ -177,14 +207,17 @@ func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { // WriteInt64 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { - if !s.opts.WriteZeroValues && v == 0 { + if v == 0 && s.skipZeroValue() { return } switch enc := s.head.Top().(type) { case *smithyjson.Object: - enc.Key(schema.MemberName()).Long(v) + enc.Key(s.jsonMemberName(schema)).Long(v) case *smithyjson.Array: enc.Value().Long(v) + case smithyjson.Value: + enc.Long(v) + s.head.Pop() default: s.root.Long(v) } @@ -192,16 +225,19 @@ func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { // WriteFloat32 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { - if !s.opts.WriteZeroValues && v == 0 { + if v == 0 && s.skipZeroValue() { return } var jv smithyjson.Value switch enc := s.head.Top().(type) { case *smithyjson.Object: - jv = enc.Key(schema.MemberName()) + jv = enc.Key(s.jsonMemberName(schema)) case *smithyjson.Array: jv = enc.Value() + case smithyjson.Value: + jv = enc + s.head.Pop() default: s.root.Float(v) return @@ -213,21 +249,24 @@ func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { jv.String("-Infinity") } else if math.IsNaN(float64(v)) { jv.String("NaN") + } else { + jv.Float(v) } } - -// WriteFloat64 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { - if !s.opts.WriteZeroValues && v == 0 { + if v == 0 && s.skipZeroValue() { return } var jv smithyjson.Value switch enc := s.head.Top().(type) { case *smithyjson.Object: - jv = enc.Key(schema.MemberName()) + jv = enc.Key(s.jsonMemberName(schema)) case *smithyjson.Array: jv = enc.Value() + case smithyjson.Value: + jv = enc + s.head.Pop() default: s.root.Double(v) return @@ -239,17 +278,19 @@ func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { jv.String("-Infinity") } else if math.IsNaN(v) { jv.String("NaN") + } else { + jv.Double(v) } } // WriteString implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { - if !s.opts.WriteZeroValues && v == "" { + if v == "" && s.skipZeroValue() { return } switch enc := s.head.Top().(type) { case *smithyjson.Object: - enc.Key(schema.MemberName()).String(v) + enc.Key(s.jsonMemberName(schema)).String(v) case *smithyjson.Array: enc.Value().String(v) case smithyjson.Value: @@ -264,7 +305,7 @@ func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { switch enc := s.head.Top().(type) { case *smithyjson.Object: - enc.Key(schema.MemberName()).Base64EncodeBytes(v) + enc.Key(s.jsonMemberName(schema)).Base64EncodeBytes(v) case *smithyjson.Array: enc.Value().Base64EncodeBytes(v) case smithyjson.Value: @@ -279,10 +320,11 @@ func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { switch enc := s.head.Top().(type) { case *smithyjson.Object: - s.head.Push(enc.Key(schema.MemberName()).Array()) + s.head.Push(enc.Key(s.jsonMemberName(schema)).Array()) case *smithyjson.Array: s.head.Push(enc.Value().Array()) case smithyjson.Value: + s.head.Pop() s.head.Push(enc.Array()) default: s.head.Push(s.root.Array()) @@ -301,10 +343,11 @@ func (s *ShapeSerializer) CloseList() { func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { switch enc := s.head.Top().(type) { case *smithyjson.Object: - s.head.Push(enc.Key(schema.MemberName()).Object()) + s.head.Push(enc.Key(s.jsonMemberName(schema)).Object()) case *smithyjson.Array: s.head.Push(enc.Value().Object()) case smithyjson.Value: + s.head.Pop() s.head.Push(enc.Object()) default: s.head.Push(s.root.Object()) @@ -361,17 +404,18 @@ func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema, v smithy.Serializable) { switch enc := s.head.Top().(type) { case *smithyjson.Object: - s.head.Push(enc.Key(schema.MemberName()).Object()) + s.head.Push(enc.Key(s.jsonMemberName(schema)).Object()) case *smithyjson.Array: s.head.Push(enc.Value().Object()) case smithyjson.Value: + s.head.Pop() s.head.Push(enc.Object()) default: s.head.Push(s.root.Object()) } top := s.head.Top().(*smithyjson.Object) - s.head.Push(top.Key(variant.MemberName())) + s.head.Push(top.Key(s.jsonMemberName(variant))) v.Serialize(s) @@ -387,7 +431,7 @@ func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema, v smithy.Serializab switch enc := s.head.Top().(type) { case *smithyjson.Object: - s.head.Push(enc.Key(schema.MemberName())) + s.head.Push(enc.Key(s.jsonMemberName(schema))) case *smithyjson.Array: s.head.Push(enc.Value()) } @@ -399,7 +443,7 @@ func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema, v smithy.Serializab func (s *ShapeSerializer) WriteNil(schema *smithy.Schema) { switch enc := s.head.Top().(type) { case *smithyjson.Object: - enc.Key(schema.MemberName()).Null() + enc.Key(s.jsonMemberName(schema)).Null() case *smithyjson.Array: enc.Value().Null() case smithyjson.Value: @@ -456,16 +500,32 @@ func (s *ShapeSerializer) WriteDocument(schema *smithy.Schema, v document.Value) } s.CloseMap() case document.Opaque: - denc := smithydocumentjson.NewEncoder() - p, _ := denc.Encode(vv.Value) + s.writeOpaqueDocument(schema, vv.Value) + case *document.Opaque: + s.writeOpaqueDocument(schema, vv.Value) + } +} + +func (s *ShapeSerializer) writeOpaqueDocument(schema *smithy.Schema, v any) { + if m, ok := v.(document.Marshaler); ok { + p, _ := m.MarshalSmithyDocument() s.writeDocumentRaw(schema, p) + return } + denc := smithydocumentjson.NewEncoder() + + // TODO(serde2): we should expose an alternative Encode() API that + // explicitly does not return errors since schema-serde Serialize is + // errorless + p, _ := denc.Encode(v) + + s.writeDocumentRaw(schema, p) } func (s *ShapeSerializer) writeDocumentRaw(schema *smithy.Schema, p []byte) { switch enc := s.head.Top().(type) { case *smithyjson.Object: - enc.Key(schema.MemberName()).Write(p) + enc.Key(s.jsonMemberName(schema)).Write(p) case *smithyjson.Array: enc.Value().Write(p) case smithyjson.Value: @@ -500,3 +560,14 @@ func (s *stack) Pop() { func (s *stack) Len() int { return len(s.values) } + +// jsonMemberName returns the JSON key for a schema member, using the +// jsonName trait if UseJSONName is enabled, otherwise the member name. +func (s *ShapeSerializer) jsonMemberName(schema *smithy.Schema) string { + if s.opts.UseJSONName { + if jn, ok := smithy.SchemaTrait[*traits.JSONName](schema); ok { + return jn.Name + } + } + return schema.MemberName() +} diff --git a/aws-protocols/restjson1/restjson1.go b/aws-protocols/restjson1/restjson1.go new file mode 100644 index 000000000..0a6a98450 --- /dev/null +++ b/aws-protocols/restjson1/restjson1.go @@ -0,0 +1,159 @@ +package restjson1 + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + + "github.com/aws/smithy-go" + internalhttpbinding "github.com/aws/smithy-go/aws-protocols/internal/httpbinding" + internaljson "github.com/aws/smithy-go/aws-protocols/internal/json" + smithyio "github.com/aws/smithy-go/io" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// New returns an instance of the aws.protocols#restJson1 protocol. +func New() *Protocol { + return &Protocol{} +} + +// Protocol implements aws.protocols#restJson1. +type Protocol struct{} + +var _ smithyhttp.ClientProtocol = (*Protocol)(nil) + +// ID identifies the protocol. +func (*Protocol) ID() string { + return "aws.protocols#restJson1" +} + +// SerializeRequest serializes a request for restJson1. +func (p *Protocol) SerializeRequest( + ctx context.Context, + op *smithy.Schema, + in smithy.Serializable, + req *smithyhttp.Request, +) error { + serializer, err := internalhttpbinding.NewShapeSerializer(op, req, internaljson.NewShapeSerializer(sUseJSONName)) + if err != nil { + return err + } + + in.Serialize(serializer) + if err := serializer.Build(in, "application/json"); err != nil { + return fmt.Errorf("build request: %w", err) + } + + return nil +} + +// DeserializeResponse deserializes a response for restJson1. +func (p *Protocol) DeserializeResponse( + ctx context.Context, + op *smithy.Schema, + types *smithy.TypeRegistry, + resp *smithyhttp.Response, + out smithy.Deserializable, +) error { + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return p.deserializeError(types, resp) + } + + // @httpPayload + @streaming = do not touch the body at all, it's the + // caller's problem + if so, ok := out.(smithy.StreamingOutput); ok { + so.SetPayloadStream(resp.Body) + deser := internalhttpbinding.NewShapeDeserializer(resp.Response, bd(nil), nil) + if err := out.Deserialize(deser); err != nil { + return &smithy.DeserializationError{Err: err} + } + return nil + } + + payload, err := io.ReadAll(resp.Body) + if err != nil { + return &smithy.DeserializationError{Err: err} + } + + deser := internalhttpbinding.NewShapeDeserializer(resp.Response, bd(payload), payload) + if err := out.Deserialize(deser); err != nil { + return &smithy.DeserializationError{Err: err} + } + + return nil +} + +func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithyhttp.Response) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := bytes.NewReader(errorBuffer.Bytes()) + + errorCode := "UnknownError" + errorMessage := errorCode + + headerCode := response.Header.Get("X-Amzn-ErrorType") + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(errorBody, ringBuffer) + decoder := json.NewDecoder(body) + decoder.UseNumber() + bodyInfo, err := internaljson.GetProtocolErrorInfo(decoder) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + errorBody.Seek(0, io.SeekStart) + if typ, ok := internaljson.ResolveProtocolErrorType(headerCode, bodyInfo); ok { + errorCode = typ + } + if len(bodyInfo.Message) != 0 { + errorMessage = bodyInfo.Message + } + + errorCode = internaljson.SanitizeErrorCode(errorCode) + + perr, ok := types.DeserializableError(errorCode) + if !ok { + return &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + } + + errorBody.Seek(0, io.SeekStart) + errorBytes, _ := io.ReadAll(errorBody) + + deser := internalhttpbinding.NewShapeDeserializer(response.Response, bd(errorBytes), errorBytes) + if err := perr.Deserialize(deser); err != nil { + return &smithy.DeserializationError{Err: err} + } + + return perr +} + +func bd(payload []byte) smithy.ShapeDeserializer { + if len(payload) == 0 { + payload = []byte("{}") + } + return internaljson.NewShapeDeserializer(payload, dUseJSONName) +} + +// TODO(serde2): can we just have one struct? +func sUseJSONName(o *internaljson.ShapeSerializerOptions) { + o.UseJSONName = true +} + +func dUseJSONName(o *internaljson.ShapeDeserializerOptions) { + o.UseJSONName = true +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java index ad1a484d4..0bcd05a65 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java @@ -60,6 +60,7 @@ import software.amazon.smithy.model.shapes.StructureShape; import software.amazon.smithy.model.shapes.UnionShape; import software.amazon.smithy.model.traits.EnumTrait; +import software.amazon.smithy.model.traits.StreamingTrait; import software.amazon.smithy.model.transform.ModelTransformer; import software.amazon.smithy.utils.OptionalUtils; import software.amazon.smithy.utils.SmithyInternalApi; @@ -268,6 +269,36 @@ void execute() { protocolDocumentGenerator.generateInternalDocumentTypes(protocolGenerator, contextBuilder.build()); } + // TODO(serde2): event streams still need all the old stuff as we're migrating over to schema-serde, once we've + // done that for event streams remove this + if (settings.useExperimentalSerde() && protocolGenerator != null && eventStreamGenerator.hasEventStreamOperations()) { + ProtocolGenerator.GenerationContext.Builder contextBuilder = ProtocolGenerator.GenerationContext.builder() + .protocolName(protocolGenerator.getProtocolName()) + .integrations(integrations) + .model(model) + .service(service) + .settings(settings) + .symbolProvider(symbolProvider) + .delegator(writers); + + writers.useFileWriter("serializers.go", settings.getModuleName(), writer -> { + ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); + protocolGenerator.generateRequestSerializers(context); + protocolGenerator.generateSharedSerializerComponents(context); + }); + + writers.useFileWriter("deserializers.go", settings.getModuleName(), writer -> { + ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); + protocolGenerator.generateResponseDeserializers(context); + protocolGenerator.generateSharedDeserializerComponents(context); + }); + + eventStreamGenerator.writeEventStreamImplementation(writer -> { + ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); + protocolGenerator.generateEventStreamComponents(context); + }); + } + LOGGER.info("Generating protocol tests for " + service.getId()); ProtocolUtils.generateHttpProtocolTests(ctx); @@ -275,7 +306,9 @@ void execute() { protocolDocumentGenerator.generateLegacyInternalDocumentTypes(ctx); var shapes = new ArrayList<>(ctx.serdeShapes()); - shapes.add(ShapeUtil.UNIT); // targeted by enum members, just generate a blank schema for it + if (!shapes.contains(ShapeUtil.UNIT)) { + shapes.add(ShapeUtil.UNIT); // targeted by enum members, just generate a blank schema for it + } ctx.writerDelegator().useFileWriter("type_registry.go", settings.getModuleName(), new TypeRegistry(ctx)); @@ -284,6 +317,11 @@ void execute() { new SchemaGenerator(ctx, shape).accept(writer); } + var operations = TopDownIndex.of(model).getContainedOperations(service); + for (OperationShape op : operations) { + new SchemaGenerator(ctx, op).accept(writer); + } + writer.write(""); writer.writeDocs("Initialize schema members after all schemas are declared to avoid initialization cycles"); writer.openBlock("func init() {", "}", () -> { @@ -295,7 +333,9 @@ void execute() { var lists = ctx.serdeShapes(ListShape.class); var maps = ctx.serdeShapes(MapShape.class); - var unionSerdes = ctx.serdeShapes(UnionShape.class); + var unionSerdes = ctx.serdeShapes(UnionShape.class).stream() + .filter(it -> !it.hasTrait(StreamingTrait.class)) + .toList(); // unfortunately since we have input/output in the top-level package and nested shapes in types/ we have to // generate these twice since we don't want to export them diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java index 667bc5987..c1bba9e6c 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java @@ -17,6 +17,7 @@ import software.amazon.smithy.model.traits.HttpQueryParamsTrait; import software.amazon.smithy.model.traits.HttpQueryTrait; import software.amazon.smithy.model.traits.HttpResponseCodeTrait; +import software.amazon.smithy.model.traits.HttpTrait; import software.amazon.smithy.model.traits.JsonNameTrait; import software.amazon.smithy.model.traits.MediaTypeTrait; import software.amazon.smithy.model.traits.SensitiveTrait; @@ -56,6 +57,10 @@ public class DefaultTraitGenerators { GENERATORS.put(StreamingTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("Streaming"))); // HTTP bindings + GENERATORS.put(HttpTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HTTP"), + "Method", t -> ((HttpTrait) t).getMethod(), + "URI", t -> ((HttpTrait) t).getUri().toString(), + "Code", t -> ((HttpTrait) t).getCode())); GENERATORS.put(HttpHeaderTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HTTPHeader"), "Name", HttpHeaderTrait::getValue)); GENERATORS.put(HttpLabelTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HTTPLabel"))); @@ -66,6 +71,8 @@ public class DefaultTraitGenerators { "Name", HttpQueryTrait::getValue)); GENERATORS.put(HttpQueryParamsTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HTTPQueryParams"))); GENERATORS.put(HttpResponseCodeTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HTTPResponseCode"))); + GENERATORS.put(HttpErrorTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HTTPError"), + "Code", HttpErrorTrait::getCode)); // Endpoint Traits GENERATORS.put(HostLabelTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("HostLabel"))); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java index 0f90f2940..b36857325 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java @@ -27,14 +27,12 @@ import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin; import software.amazon.smithy.model.Model; -import software.amazon.smithy.model.knowledge.EventStreamIndex; import software.amazon.smithy.model.knowledge.OperationIndex; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.OperationShape; import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.shapes.StructureShape; -import software.amazon.smithy.model.shapes.UnionShape; import software.amazon.smithy.model.traits.DeprecatedTrait; import software.amazon.smithy.model.traits.StreamingTrait; import software.amazon.smithy.rulesengine.traits.EndpointBddTrait; @@ -155,7 +153,7 @@ public void run() { if (EventStreamGenerator.isV2EventStream(model, operation)) { List onlyEventStreamMembers = outputShape.members().stream().filter(member -> StreamingTrait.isEventStream(model, member)).toList(); - structOutputShape = buildShape(onlyEventStreamMembers); + structOutputShape = withMembers(outputShape, onlyEventStreamMembers); } else { structOutputShape = outputShape; } @@ -291,14 +289,16 @@ private void generateOperationProtocolMiddlewareAdders() { writer.write("err = stack.Deserialize.Add(&$L{}, middleware.After)", deserializerMiddlewareName); writer.write("if err != nil { return err }"); } else { + writer.addUseImports(SmithyGoDependency.SMITHY); + var opSchemaName = "schemas." + SchemaGenerator.getSchemaName(operation, service); writer.write(""" - if err := stack.Serialize.Add(&serializeRequestMiddleware{options: &options}, middleware.After); err != nil { + if err := stack.Serialize.Add(&serializeRequestMiddleware{options: &options, operationSchema: $L}, middleware.After); err != nil { return err - }"""); + }""", opSchemaName); writer.write(""" - if err := stack.Deserialize.Add(&deserializeResponseMiddleware{options: &options, output: &$T{}}, middleware.After); err != nil { + if err := stack.Deserialize.Add(&deserializeResponseMiddleware{options: &options, operationSchema: $L, output: &$T{}}, middleware.After); err != nil { return err - }""", symbolProvider.toSymbol(output)); + }""", opSchemaName, symbolProvider.toSymbol(output)); } // FUTURE: retry middleware should be at the front of finalize, right now it's added by the SDK @@ -322,8 +322,8 @@ public static String getAddOperationMiddlewareFuncName(Symbol operation) { return String.format("addOperation%sMiddlewares", operation.getName()); } - private StructureShape buildShape(List members) { - var struct = StructureShape.builder().id(ShapeId.from("synthetic#throwaway")); + private StructureShape withMembers(StructureShape shape, List members) { + var struct = StructureShape.builder().id(shape.getId()); members.stream().forEach(member -> struct.addMember( MemberShape.builder() .id(struct.getId().withMember(member.getMemberName())) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java index 6878cf20d..6941cda6b 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java @@ -6,6 +6,7 @@ import java.util.Map; import software.amazon.smithy.go.codegen.util.ShapeUtil; import software.amazon.smithy.model.shapes.MemberShape; +import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.traits.Trait; import software.amazon.smithy.utils.SmithyInternalApi; @@ -21,19 +22,20 @@ public SchemaGenerator(GoCodegenContext ctx, Shape shape) { this.shape = shape; } - public static String getSchemaName(Shape shape) { - var name = shape.getId().getName(); + public static String getSchemaName(Shape shape, ServiceShape service) { + var name = shape.getId().getName(service); return ShapeUtil.isExported(shape) ? name : "_" + name; } - public static String getMemberSchemaName(Shape shape, MemberShape member) { - return String.format("%s_%s", getSchemaName(shape), member.getMemberName()); + public static String getMemberSchemaName(Shape shape, MemberShape member, ServiceShape service) { + return String.format("%s_%s", getSchemaName(shape, service), member.getMemberName()); } @Override public void accept(GoWriter writer) { + var service = ctx.service(); writer.addUseImports(SmithyGoDependency.SMITHY); writer.writeGoTemplate(""" var $ident:L = smithy.NewSchema(smithy.ShapeID{ @@ -43,7 +45,7 @@ public void accept(GoWriter writer) { $memberVarDecls:W """, Map.of( - "ident", getSchemaName(shape), + "ident", getSchemaName(shape, service), "namespace", shape.getId().getNamespace(), "name", shape.getId().getName(), "type", StringUtils.capitalize(shape.getType().toString()), @@ -69,21 +71,22 @@ private Writable renderMemberVarDecl(MemberShape member) { return goTemplate(""" var $ident:L *smithy.Schema """, - Map.of("ident", getMemberSchemaName(shape, member))); + Map.of("ident", getMemberSchemaName(shape, member, ctx.service()))); } private Writable renderMemberAddAndAssign(MemberShape member) { var target = ctx.model().expectShape(member.getTarget()); + var service = ctx.service(); var memberTraits = member.getAllTraits().values(); return goTemplate(""" $ident:L = $schema:L.AddMember($name:S, $target:L$traits:W) """, Map.of( - "ident", getMemberSchemaName(shape, member), - "schema", getSchemaName(shape), + "ident", getMemberSchemaName(shape, member, service), + "schema", getSchemaName(shape, service), "name", member.getMemberName(), - "target", getSchemaName(target), + "target", getSchemaName(target, service), "traits", renderVariadicTraits(memberTraits) )); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java index b3803ed71..706e3e8ad 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java @@ -15,6 +15,7 @@ package software.amazon.smithy.go.codegen; +import static software.amazon.smithy.go.codegen.GoSettings.PROTOCOLS_BY_PRIORITY; import static software.amazon.smithy.go.codegen.GoWriter.autoDocTemplate; import static software.amazon.smithy.go.codegen.GoWriter.emptyGoTemplate; import static software.amazon.smithy.go.codegen.GoWriter.goDocTemplate; @@ -24,8 +25,13 @@ import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import software.amazon.smithy.aws.traits.protocols.AwsJson1_0Trait; +import software.amazon.smithy.aws.traits.protocols.RestJson1Trait; +import software.amazon.smithy.codegen.core.CodegenException; +import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.codegen.core.SymbolProvider; import software.amazon.smithy.go.codegen.auth.AuthSchemeResolverGenerator; import software.amazon.smithy.go.codegen.auth.GetIdentityMiddlewareGenerator; @@ -254,7 +260,23 @@ func New(options $options:L, optFns ...func(*$options:L)) *$client:L { private Writable generateExperimentalSerdeResolvers() { ensureSupportedProtocol(); // TODO(serde2) dynamically resolve a symbol based on traits - return goTemplate("options.Protocol = $T()", SmithyGoDependency.SMITHY_AWS_PROTOCOLS_JSON10.func("New")); + return goTemplate("options.Protocol = $T()", resolveDefaultProtocol()); + } + + private Symbol resolveDefaultProtocol() { + Set protocols = ServiceIndex.of(model).getProtocols(service).keySet(); + var preferred = PROTOCOLS_BY_PRIORITY.stream() + .filter(protocols::contains) + .findFirst() + .get(); + + if (preferred.equals(AwsJson1_0Trait.ID)) { + return SmithyGoDependency.SMITHY_AWS_PROTOCOLS_JSON10.func("New"); + } else if (preferred.equals(RestJson1Trait.ID)) { + return SmithyGoDependency.SMITHY_AWS_PROTOCOLS_RESTJSON1.func("New"); + } else { + throw new CodegenException("unsupported schema-serde protocol " + preferred); + } } private Writable generateGetOptions() { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SimpleTraitGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SimpleTraitGenerator.java index 49e22d6ae..5abcc3913 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SimpleTraitGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SimpleTraitGenerator.java @@ -32,6 +32,17 @@ public SimpleTraitGenerator(Symbol symbol, mappings.put(k2, v2); } + public SimpleTraitGenerator(Symbol symbol, + String k1, Function v1, + String k2, Function v2, + String k3, Function v3) { + this.symbol = symbol; + + mappings.put(k1, v1); + mappings.put(k2, v2); + mappings.put(k3, v3); + } + @Override public Writable render(Trait trait) { return goTemplate("&$T{$W}", symbol, Writable.map(mappings.entrySet(), entry -> { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java index df88a7041..d42c10437 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java @@ -83,6 +83,7 @@ public final class SmithyGoDependency { public static final GoDependency SMITHY_AWS_PROTOCOLS = relativePackage( "github.com/aws/smithy-go/aws-protocols", null, "v1.0.0", null); public static final GoDependency SMITHY_AWS_PROTOCOLS_JSON10 = relativePackage( "github.com/aws/smithy-go/aws-protocols", "awsjson10", "v1.0.0", null); + public static final GoDependency SMITHY_AWS_PROTOCOLS_RESTJSON1 = relativePackage( "github.com/aws/smithy-go/aws-protocols", "restjson1", "v1.0.0", null); public static final GoDependency MATH = stdlib("math"); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java index 773c1ea96..b5924ac76 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java @@ -16,6 +16,7 @@ package software.amazon.smithy.go.codegen; import java.util.Map; +import java.util.Optional; import java.util.Set; import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.codegen.core.SymbolProvider; @@ -25,8 +26,10 @@ import software.amazon.smithy.model.Model; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.ServiceShape; +import software.amazon.smithy.model.shapes.ShapeType; import software.amazon.smithy.model.shapes.StructureShape; import software.amazon.smithy.model.traits.ErrorTrait; +import software.amazon.smithy.model.traits.HttpPayloadTrait; import software.amazon.smithy.model.traits.InputTrait; import software.amazon.smithy.model.traits.OutputTrait; import software.amazon.smithy.model.traits.StreamingTrait; @@ -146,7 +149,10 @@ public void renderStructure(Runnable runnable, boolean isInputStructure) { writer.closeBlock("}").write(""); - if (useExperimentalSerde) { + // Only generate serde methods when the symbol matches the shape's natural symbol. + // When a symbol override is provided (e.g. for synthetic shapes like event stream + // initial reply structs), skip serde to avoid duplicate method declarations. + if (useExperimentalSerde && symbol.getName().equals(ctx.symbolProvider().toSymbol(shape).getName())) { if (shape.hasTrait(InputTrait.class)) { writer.write(new StructureSerializer(ctx, shape)); } else if (shape.hasTrait(OutputTrait.class)) { @@ -155,9 +161,31 @@ public void renderStructure(Runnable runnable, boolean isInputStructure) { writer.write(new StructureSerializer(ctx, shape)); writer.write(new StructureDeserializer(ctx, shape)); } + + getStreamingPayloadMember().ifPresent(member -> { + var memberName = symbolProvider.toMemberName(member); + writer.addUseImports(SmithyGoDependency.SMITHY); + writer.addUseImports(SmithyGoDependency.IO); + writer.write(""" + func (v *$1L) GetPayloadStream() io.Reader { return v.$2L } + var _ smithy.StreamingInput = (*$1L)(nil) + func (v *$1L) SetPayloadStream(r io.ReadCloser) { v.$2L = r } + var _ smithy.StreamingOutput = (*$1L)(nil) + """, symbol.getName(), memberName); + }); } } + private Optional getStreamingPayloadMember() { + return shape.getAllMembers().values().stream() + .filter(m -> m.hasTrait(HttpPayloadTrait.class)) + .filter(m -> { + var target = model.expectShape(m.getTarget()); + return target.getType() == ShapeType.BLOB && target.hasTrait(StreamingTrait.class); + }) + .findFirst(); + } + /** * Renders an error structure and supporting methods. */ diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java index 7719635b2..7e0298f4e 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java @@ -33,6 +33,6 @@ private Writable renderEntry(StructureShape shape) { $S: &smithy.TypeRegistryEntry{ Schema: schemas.$L, New: func() any { return &$T{} }, - },""", shape.getId().toString(), SchemaGenerator.getSchemaName(shape), ctx.symbolProvider().toSymbol(shape)); + },""", shape.getId().toString(), SchemaGenerator.getSchemaName(shape, ctx.service()), ctx.symbolProvider().toSymbol(shape)); } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java index d04147520..395ade1cb 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java @@ -113,7 +113,7 @@ public void generateUnion(GoWriter writer) { private void generateMemberSerializer(GoWriter writer, MemberShape member, String memberName, Shape target) { writer.addUseImports(SmithyGoDependency.SMITHY); writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); - var schemaName = "schemas." + SchemaGenerator.getMemberSchemaName(shape, member); + var schemaName = "schemas." + SchemaGenerator.getMemberSchemaName(shape, member, ctx.service()); writer.openBlock("func (v *$L) Serialize(s smithy.ShapeSerializer) {", "}", memberName, () -> { switch (target.getType()) { case BYTE -> writer.write("s.WriteInt8($L, v.Value)", schemaName); @@ -140,7 +140,7 @@ private void generateMemberSerializer(GoWriter writer, MemberShape member, Strin private void generateMemberDeserializer(GoWriter writer, MemberShape member, String memberName, Shape target) { writer.addUseImports(SmithyGoDependency.SMITHY); writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); - var schemaName = "schemas." + SchemaGenerator.getMemberSchemaName(shape, member); + var schemaName = "schemas." + SchemaGenerator.getMemberSchemaName(shape, member, ctx.service()); writer.openBlock("func (v *$L) Deserialize(d smithy.ShapeDeserializer) error {", "}", memberName, () -> { switch (target.getType()) { case BYTE -> writer.write("return d.ReadInt8($L, &v.Value)", schemaName); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java index 062d02f9e..9d1315527 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java @@ -7,6 +7,7 @@ import software.amazon.smithy.go.codegen.GoCodegenContext; import software.amazon.smithy.go.codegen.GoUniverseTypes; import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.ProtocolDocumentGenerator; import software.amazon.smithy.go.codegen.SmithyGoDependency; import software.amazon.smithy.go.codegen.Writable; import software.amazon.smithy.go.codegen.util.ShapeUtil; @@ -28,7 +29,7 @@ public ListDeserializer(GoCodegenContext ctx, ListShape shape) { @Override public void accept(GoWriter writer) { writer.addUseImports(SmithyGoDependency.SMITHY); - if (member.hasTrait(SparseTrait.class)) { + if (shape.hasTrait(SparseTrait.class)) { renderSparse(writer); } else { renderDense(writer); @@ -54,13 +55,11 @@ private void renderDense(GoWriter writer) { "memberSymbol", switch (member.getType()) { case ENUM -> GoUniverseTypes.String; case INT_ENUM -> GoUniverseTypes.Int32; + case DOCUMENT -> SmithyGoDependency.SMITHY_DOCUMENT.valueSymbol("Value"); default -> ctx.symbolProvider().toSymbol(member); }, "deserializeMember", renderDeserializeMember(), - "cast", switch (member.getType()) { - case ENUM, INT_ENUM -> goTemplate("$T(vv)", ctx.symbolProvider().toSymbol(member)); - default -> goTemplate("vv"); - } + "cast", renderDenseCast() )); } @@ -90,6 +89,7 @@ private void renderSparse(GoWriter writer) { "memberSymbol", switch (member.getType()) { case ENUM -> GoUniverseTypes.String; case INT_ENUM -> GoUniverseTypes.Int32; + case DOCUMENT -> SmithyGoDependency.SMITHY_DOCUMENT.valueSymbol("Value"); default -> ctx.symbolProvider().toSymbol(member); }, "deserializeMember", renderDeserializeMember(), @@ -106,13 +106,35 @@ private Writable renderSparseCast() { }()""", ctx.symbolProvider().toSymbol(member)); // don't need the address-of - case BLOB, LIST, SET, MAP, UNION, DOCUMENT -> + case BLOB, LIST, SET, MAP, UNION -> goTemplate("vv"); + case DOCUMENT -> renderDocumentCast(); + default -> goTemplate("&vv"); }; } + private Writable renderDenseCast() { + return switch (member.getType()) { + case ENUM, INT_ENUM -> goTemplate("$T(vv)", ctx.symbolProvider().toSymbol(member)); + case DOCUMENT -> renderDocumentCast(); + default -> goTemplate("vv"); + }; + } + + private Writable renderDocumentCast() { + var unmarshaler = ProtocolDocumentGenerator.Utilities.getInternalDocumentSymbolBuilder( + ctx.settings(), ProtocolDocumentGenerator.INTERNAL_NEW_DOCUMENT_UNMARSHALER_FUNC).build(); + return goTemplate(""" + func() $T { + if ov, ok := vv.(smithydocument.Opaque); ok { + return $T(ov.Value) + } + return nil + }()""", ctx.symbolProvider().toSymbol(member), unmarshaler); + } + private Writable renderDeserializeMember() { return switch (member.getType()) { case BYTE -> @@ -134,7 +156,7 @@ private Writable renderDeserializeMember() { case BOOLEAN -> goTemplate("d.ReadBool(s.ListMember(), &vv)"); case TIMESTAMP -> - goTemplate("d.ReadTimestamp(s.ListMember(), &vv)"); + goTemplate("d.ReadTime(s.ListMember(), &vv)"); case BLOB -> goTemplate("d.ReadBlob(s.ListMember(), &vv)"); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java index 864cd9ed0..0b4d65801 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java @@ -27,8 +27,14 @@ public ListSerializer(GoCodegenContext ctx, ListShape shape) { @Override public void accept(GoWriter writer) { writer.addUseImports(SmithyGoDependency.SMITHY); + if (member.getType() == software.amazon.smithy.model.shapes.ShapeType.DOCUMENT) { + writer.addUseImports(SmithyGoDependency.SMITHY_DOCUMENT); + } writer.writeGoTemplate(""" func serialize$shapeName:L(s smithy.ShapeSerializer, schema *smithy.Schema, v $symbol:T) { + if v == nil { + return + } s.WriteList(schema) for _, vv := range v { $serializeValue:W @@ -92,7 +98,7 @@ private Writable renderSparseSerializeValue() { case STRUCTURE -> wrapNilCheck(goTemplate("vv.Serialize(s)")); case DOCUMENT -> - wrapNilCheck(goTemplate("s.WriteDocument(schema.ListMember(), vv)")); + wrapNilCheck(goTemplate("s.WriteDocument(schema.ListMember(), &smithydocument.Opaque{Value: vv})")); case BIG_INTEGER, BIG_DECIMAL -> throw new CodegenException("big integer / big decimal unsupported"); @@ -131,7 +137,7 @@ private Writable renderDenseSerializeValue() { case STRUCTURE -> goTemplate("vv.Serialize(s)"); case DOCUMENT -> - goTemplate("s.WriteDocument(schema.ListMember(), vv)"); + goTemplate("s.WriteDocument(schema.ListMember(), &smithydocument.Opaque{Value: vv})"); case BIG_INTEGER, BIG_DECIMAL -> throw new CodegenException("big integer / big decimal unsupported"); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java index 36b7f5ecc..a383eb4c9 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java @@ -7,6 +7,7 @@ import software.amazon.smithy.go.codegen.GoCodegenContext; import software.amazon.smithy.go.codegen.GoUniverseTypes; import software.amazon.smithy.go.codegen.GoWriter; +import software.amazon.smithy.go.codegen.ProtocolDocumentGenerator; import software.amazon.smithy.go.codegen.SmithyGoDependency; import software.amazon.smithy.go.codegen.Writable; import software.amazon.smithy.go.codegen.util.ShapeUtil; @@ -55,13 +56,11 @@ private void renderDense(GoWriter writer) { "valueSymbol", switch (value.getType()) { case ENUM -> GoUniverseTypes.String; case INT_ENUM -> GoUniverseTypes.Int32; + case DOCUMENT -> SmithyGoDependency.SMITHY_DOCUMENT.valueSymbol("Value"); default -> ctx.symbolProvider().toSymbol(value); }, "deserializeValue", renderDeserializeValue(), - "cast", switch (value.getType()) { - case ENUM, INT_ENUM -> goTemplate("$T(vv)", ctx.symbolProvider().toSymbol(value)); - default -> goTemplate("vv"); - } + "cast", renderDenseCast() )); } @@ -92,6 +91,7 @@ private void renderSparse(GoWriter writer) { "valueSymbol", switch (value.getType()) { case ENUM -> GoUniverseTypes.String; case INT_ENUM -> GoUniverseTypes.Int32; + case DOCUMENT -> SmithyGoDependency.SMITHY_DOCUMENT.valueSymbol("Value"); default -> ctx.symbolProvider().toSymbol(value); }, "deserializeValue", renderDeserializeValue(), @@ -108,13 +108,35 @@ private Writable renderSparseCast() { }()""", ctx.symbolProvider().toSymbol(value)); // don't need the address-of - case BLOB, LIST, SET, MAP, UNION, DOCUMENT -> + case BLOB, LIST, SET, MAP, UNION -> goTemplate("vv"); + case DOCUMENT -> renderDocumentCast(); + default -> goTemplate("&vv"); }; } + private Writable renderDenseCast() { + return switch (value.getType()) { + case ENUM, INT_ENUM -> goTemplate("$T(vv)", ctx.symbolProvider().toSymbol(value)); + case DOCUMENT -> renderDocumentCast(); + default -> goTemplate("vv"); + }; + } + + private Writable renderDocumentCast() { + var unmarshaler = ProtocolDocumentGenerator.Utilities.getInternalDocumentSymbolBuilder( + ctx.settings(), ProtocolDocumentGenerator.INTERNAL_NEW_DOCUMENT_UNMARSHALER_FUNC).build(); + return goTemplate(""" + func() $T { + if ov, ok := vv.(smithydocument.Opaque); ok { + return $T(ov.Value) + } + return nil + }()""", ctx.symbolProvider().toSymbol(value), unmarshaler); + } + private Writable renderDeserializeValue() { return switch (value.getType()) { case BYTE -> diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java index be4953f7a..988cc2932 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java @@ -7,6 +7,7 @@ import software.amazon.smithy.go.codegen.GoCodegenContext; import software.amazon.smithy.go.codegen.GoWriter; import software.amazon.smithy.go.codegen.SmithyGoDependency; +import software.amazon.smithy.go.codegen.SmithyGoTypes; import software.amazon.smithy.go.codegen.Writable; import software.amazon.smithy.go.codegen.util.ShapeUtil; import software.amazon.smithy.model.shapes.MapShape; @@ -27,8 +28,14 @@ public MapSerializer(GoCodegenContext ctx, MapShape shape) { @Override public void accept(GoWriter writer) { writer.addUseImports(SmithyGoDependency.SMITHY); + if (value.getType() == software.amazon.smithy.model.shapes.ShapeType.DOCUMENT) { + writer.addUseImports(SmithyGoDependency.SMITHY_DOCUMENT); + } writer.writeGoTemplate(""" func serialize$shapeName:L(s smithy.ShapeSerializer, schema *smithy.Schema, v $symbol:T) { + if v == nil { + return + } s.WriteMap(schema) for k, vv := range v { s.WriteKey(schema.MapKey(), k) @@ -93,7 +100,7 @@ private Writable renderSparseSerializeValue() { case STRUCTURE -> wrapNilCheck(goTemplate("vv.Serialize(s)")); case DOCUMENT -> - wrapNilCheck(goTemplate("s.WriteDocument(schema.MapValue(), vv)")); + wrapNilCheck(goTemplate("s.WriteDocument(schema.MapValue(), &smithydocument.Opaque{Value: vv})")); case BIG_INTEGER, BIG_DECIMAL -> throw new CodegenException("big integer / big decimal unsupported"); @@ -132,7 +139,7 @@ private Writable renderDenseSerializeValue() { case STRUCTURE -> goTemplate("vv.Serialize(s)"); case DOCUMENT -> - goTemplate("s.WriteDocument(schema.MapValue(), vv)"); + goTemplate("s.WriteDocument(schema.MapValue(), &smithydocument.Opaque{Value: vv})"); case BIG_INTEGER, BIG_DECIMAL -> throw new CodegenException("big integer / big decimal unsupported"); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2DeserializeResponseMiddleware.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2DeserializeResponseMiddleware.java index 4763a6ccd..f8f6019eb 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2DeserializeResponseMiddleware.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2DeserializeResponseMiddleware.java @@ -25,6 +25,7 @@ public String getId() { public Map getFields() { return Map.of( "options", pointerTo(buildPackageSymbol("Options")), + "operationSchema", SmithyGoDependency.SMITHY.pointableSymbol("Schema"), "output", SmithyGoDependency.SMITHY.interfaceSymbol("Deserializable") ); } @@ -45,7 +46,7 @@ public Writable getFuncBody() { _, span := tracing.StartSpan(ctx, "OperationDeserializer") endTimer := startMetricTimer(ctx, "client.call.deserialization_duration") - err = m.options.Protocol.DeserializeResponse(ctx, TypeRegistry, resp, m.output) + err = m.options.Protocol.DeserializeResponse(ctx, m.operationSchema, TypeRegistry, resp, m.output) out.Result = m.output endTimer() diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2SerializeRequestMiddleware.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2SerializeRequestMiddleware.java index c5608ecbb..e85533988 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2SerializeRequestMiddleware.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2SerializeRequestMiddleware.java @@ -25,7 +25,8 @@ public String getId() { @Override public Map getFields() { return Map.of( - "options", pointerTo(buildPackageSymbol("Options")) + "options", pointerTo(buildPackageSymbol("Options")), + "operationSchema", SmithyGoDependency.SMITHY.pointableSymbol("Schema") ); } @@ -46,7 +47,7 @@ public Writable getFuncBody() { _, span := tracing.StartSpan(ctx, "OperationSerializer") endTimer := startMetricTimer(ctx, "client.call.serialization_duration") - err := m.options.Protocol.SerializeRequest(ctx, input, req) + err := m.options.Protocol.SerializeRequest(ctx, m.operationSchema, input, req) endTimer() span.End() diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java index 9ed2c6343..5fd167035 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java @@ -13,6 +13,7 @@ import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.traits.StreamingTrait; public class StructureDeserializer implements Writable { private final GoCodegenContext ctx; @@ -33,13 +34,15 @@ public void accept(GoWriter writer) { var symbol = ctx.symbolProvider().toSymbol(shape); var members = shape.members().stream() + .filter(m -> !StreamingTrait.isEventStream(ctx.model(), m)) + .filter(m -> !ctx.model().expectShape(m.getTarget()).hasTrait(StreamingTrait.class)) .sorted(Comparator.comparing(MemberShape::getMemberName)) .toList(); writer.openBlock("func (v *$L) Deserialize(d smithy.ShapeDeserializer) error {", "}", symbol.getName(), () -> { - writer.openBlock("return smithy.ReadStruct(d, schemas.$L, func(s *smithy.Schema) error {", "})", SchemaGenerator.getSchemaName(shape), () -> { + writer.openBlock("return smithy.ReadStruct(d, schemas.$L, func(s *smithy.Schema) error {", "})", SchemaGenerator.getSchemaName(shape, ctx.service()), () -> { writer.openBlock("switch s {", "}", () -> { for (var member : members) { - writer.write("case schemas.$L:", SchemaGenerator.getMemberSchemaName(shape, member)); + writer.write("case schemas.$L:", SchemaGenerator.getMemberSchemaName(shape, member, ctx.service())); renderMember(writer, member, ctx.model().expectShape(member.getTarget()), "v." + ctx.symbolProvider().toMemberName(member)); } }); @@ -49,7 +52,7 @@ public void accept(GoWriter writer) { } private void renderMember(GoWriter writer, MemberShape member, Shape target, String ident) { - var schemaName = SchemaGenerator.getMemberSchemaName(shape, member); + var schemaName = SchemaGenerator.getMemberSchemaName(shape, member, ctx.service()); var ptrSuffix = nilIndex.isNillable(member) ? "Ptr" : ""; switch (target.getType()) { case BYTE -> diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java index 335dc386c..def9e1729 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java @@ -13,6 +13,7 @@ import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.traits.StreamingTrait; import software.amazon.smithy.utils.SmithyInternalApi; @SmithyInternalApi @@ -34,11 +35,13 @@ public void accept(GoWriter writer) { var symbol = ctx.symbolProvider().toSymbol(shape); var members = shape.members().stream() + .filter(m -> !StreamingTrait.isEventStream(ctx.model(), m)) + .filter(m -> !ctx.model().expectShape(m.getTarget()).hasTrait(StreamingTrait.class)) .sorted(Comparator.comparing(MemberShape::getMemberName)) .toList(); writer.addUseImports(SmithyGoDependency.SMITHY); writer.openBlock("func (v *$L) Serialize(s smithy.ShapeSerializer) {", "}", symbol.getName(), () -> { - writer.write("s.WriteMap(schemas.$L)", SchemaGenerator.getSchemaName(shape)); + writer.write("s.WriteMap(schemas.$L)", SchemaGenerator.getSchemaName(shape, ctx.service())); for (var member : members) { var target = ShapeUtil.expectMember(ctx.model(), shape, member.getMemberName()); var ident = String.format("v.%s", ctx.symbolProvider().toMemberName(member)); @@ -50,7 +53,7 @@ public void accept(GoWriter writer) { } private void generateSerializeMember(GoWriter writer, MemberShape member, Shape target, String ident) { - var schemaName = "schemas." + SchemaGenerator.getMemberSchemaName(shape, member); + var schemaName = "schemas." + SchemaGenerator.getMemberSchemaName(shape, member, ctx.service()); var ptrSuffix = nilIndex.isNillable(member) ? "Ptr" : ""; switch (target.getType()) { case BYTE -> diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java index 938c83a0a..3c35b6e84 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java @@ -55,7 +55,7 @@ private Writable renderCases(java.util.List members) { var unionSymbol = ctx.symbolProvider().toSymbol(shape); for (var member : members) { var memberName = ctx.symbolProvider().toMemberName(member); - var variantSchema = SchemaGenerator.getMemberSchemaName(shape, member); + var variantSchema = SchemaGenerator.getMemberSchemaName(shape, member, ctx.service()); var memberSymbol = unionSymbol.toBuilder() .name(memberName) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionSerializer.java index 9a5474c61..c91889251 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionSerializer.java @@ -53,7 +53,7 @@ private Writable renderCases(List members) { .name(ctx.symbolProvider().toMemberName(member)) .namespace(ctx.settings().getModuleName() + "/types", ".") .build(); - var variantSchema = SchemaGenerator.getMemberSchemaName(shape, member); + var variantSchema = SchemaGenerator.getMemberSchemaName(shape, member, ctx.service()); w.write("case *$T:", variantSymbol); w.write(" s.WriteUnion(schema, schemas.$L, vv)", variantSchema); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java index a60c6aea1..ac4468090 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java @@ -29,6 +29,7 @@ import software.amazon.smithy.model.shapes.ShapeType; import software.amazon.smithy.model.shapes.StringShape; import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.traits.UnitTypeTrait; public final class ShapeUtil { public static final StringShape STRING_SHAPE = StringShape.builder() @@ -45,13 +46,14 @@ public final class ShapeUtil { public static final StructureShape UNIT = StructureShape.builder() .id("smithy.api#Unit") + .addTrait(new UnitTypeTrait()) .build(); private ShapeUtil() {} public static boolean isExported(Shape shape) { return switch (shape.getType()) { - case STRUCTURE, UNION, ENUM -> true; + case OPERATION, STRUCTURE, UNION, ENUM -> true; default -> false; }; } diff --git a/schema.go b/schema.go index 7cb55bab7..926653869 100644 --- a/schema.go +++ b/schema.go @@ -90,10 +90,13 @@ func NewSchema(id ShapeID, typ ShapeType, numMembers int, traits ...Trait) *Sche // reference. func (s *Schema) AddMember(name string, target *Schema, traits ...Trait) *Schema { m := &Schema{ - id: ShapeID{Member: name}, - typ: target.typ, - members: target.members, - traits: maps.Clone(target.traits), + id: ShapeID{Member: name}, + typ: target.typ, + members: target.members, + traits: maps.Clone(target.traits), + listMember: target.listMember, + mapKey: target.mapKey, + mapValue: target.mapValue, } if len(m.traits) == 0 && len(traits) != 0 { @@ -135,11 +138,21 @@ func (s *Schema) MemberName() string { return s.id.Member } +// Type returns the shape type of the schema. +func (s *Schema) Type() ShapeType { + return s.typ +} + // Member returns the member schema for the given name, or nil. func (s *Schema) Member(name string) *Schema { return s.members[name] } +// Members returns the schema's members as a map of name to schema. +func (s *Schema) Members() map[string]*Schema { + return s.members +} + // Trait returns the target trait on the schema if it exists. func SchemaTrait[T Trait](s *Schema) (T, bool) { var trait T diff --git a/serde.go b/serde.go index cdd41e0b8..f9320ba08 100644 --- a/serde.go +++ b/serde.go @@ -1,6 +1,7 @@ package smithy import ( + "io" "math/big" "time" @@ -133,6 +134,18 @@ type Serializable interface { Serialize(ShapeSerializer) } +// StreamingInput is implemented by input types that have a streaming blob +// payload (an io.Reader member with @httpPayload + @streaming). +type StreamingInput interface { + GetPayloadStream() io.Reader +} + +// StreamingOutput is implemented by output types that have a streaming blob +// payload (an io.ReadCloser member with @httpPayload + @streaming). +type StreamingOutput interface { + SetPayloadStream(io.ReadCloser) +} + // Deserializable is an entity that can unmarshal itself from a // ShapeDeserializer. type Deserializable interface { diff --git a/traits/http.go b/traits/http.go index f9a544f8e..2575519d2 100644 --- a/traits/http.go +++ b/traits/http.go @@ -47,3 +47,21 @@ type HTTPResponseCode struct{} // TraitID identifies the trait. func (*HTTPResponseCode) TraitID() string { return "smithy.api#httpResponseCode" } + +// HTTP represents smithy.api#http. +type HTTP struct { + Method string + URI string + Code int +} + +// TraitID identifies the trait. +func (*HTTP) TraitID() string { return "smithy.api#http" } + +// HTTPError represents smithy.api#httpError. +type HTTPError struct { + Code int +} + +// TraitID identifies the trait. +func (*HTTPError) TraitID() string { return "smithy.api#httpError" } diff --git a/transport/http/protocol.go b/transport/http/protocol.go index 4e6569d59..3d3e5bca7 100644 --- a/transport/http/protocol.go +++ b/transport/http/protocol.go @@ -14,6 +14,6 @@ import ( // protocols implemented as part of the Smithy client runtime. type ClientProtocol interface { ID() string - SerializeRequest(context.Context, smithy.Serializable, *Request) error - DeserializeResponse(ctx context.Context, types *smithy.TypeRegistry, resp *Response, out smithy.Deserializable) error + SerializeRequest(context.Context, *smithy.Schema, smithy.Serializable, *Request) error + DeserializeResponse(ctx context.Context, schema *smithy.Schema, types *smithy.TypeRegistry, resp *Response, out smithy.Deserializable) error } diff --git a/type_registry.go b/type_registry.go index c3a9165b7..3c4e02a18 100644 --- a/type_registry.go +++ b/type_registry.go @@ -31,6 +31,15 @@ func (t *TypeRegistry) DeserializableError(id string) (DeserializableError, bool return typeRegistryLookup[DeserializableError](t, id) } +// LookupEntry returns the registry entry for the given shape ID. +func (t *TypeRegistry) LookupEntry(id string) (*TypeRegistryEntry, bool) { + entry, ok := t.Entries[id] + if !ok { + entry, ok = t.lookupShortName(id) + } + return entry, ok +} + // TypeRegistryEntry holds the schema and constructor for a registered shape. type TypeRegistryEntry struct { Schema *Schema From 2219ef97c7fb5f92a007cece1526afc86aa56959 Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Tue, 17 Mar 2026 15:14:12 -0400 Subject: [PATCH 06/38] add awsjson11, fix some nil bugs in json serde (#640) --- aws-protocols/awsjson10/awsjson10.go | 22 +++++-- .../internal/json/shape_deserializer.go | 64 ++++++++----------- .../internal/json/shape_serializer.go | 4 ++ .../smithy/go/codegen/ServiceGenerator.java | 3 + .../go/codegen/serde2/MapSerializer.java | 1 - .../codegen/serde2/StructureDeserializer.java | 1 - 6 files changed, 53 insertions(+), 42 deletions(-) diff --git a/aws-protocols/awsjson10/awsjson10.go b/aws-protocols/awsjson10/awsjson10.go index d5152d1fd..04a445b4a 100644 --- a/aws-protocols/awsjson10/awsjson10.go +++ b/aws-protocols/awsjson10/awsjson10.go @@ -17,19 +17,30 @@ import ( // New returns an instance of the awsJson 1.0 protocol. func New() *Protocol { - return &Protocol{} + return &Protocol{version: "1.0"} +} + +// New11 returns an instance of the awsJson 1.1 protocol. +// +// TODO(serde2): figure out how we want to organize awsjson10 vs 11 +func New11() *Protocol { + return &Protocol{version: "1.1"} } // Protocol implements aws.protocols#awsJson10. type Protocol struct { + version string UseQueryCompatible bool } var _ smithyhttp.ClientProtocol = (*Protocol)(nil) // ID identifies the protocol. -func (*Protocol) ID() string { - return "aws.protocols#awsJson10" +func (p *Protocol) ID() string { + if p.version == "1.1" { + return "aws.protocols#awsJson1_1" + } + return "aws.protocols#awsJson1_0" } // SerializeRequest serializes a request for AWS Json 1.0. @@ -41,7 +52,7 @@ func (p *Protocol) SerializeRequest( ) error { req.Method = http.MethodPost req.Header.Set("X-Amz-Target", fmt.Sprintf("%s.%s", middleware.GetServiceName(ctx), middleware.GetOperationName(ctx))) - req.Header.Set("Content-Type", "application/x-amz-json-1.0") + req.Header.Set("Content-Type", "application/x-amz-json-"+p.version) if p.UseQueryCompatible { req.Header.Set("X-Amzn-Query-Compatible", "true") } @@ -87,6 +98,9 @@ func (p *Protocol) DeserializeResponse( return nil } +// this handles both awsJson 1.0 and 1.1 - the only thing that 1.1 adds is +// error shape renaming (basically not having the namespace) but both versions +// of the protocol are supposed to support this anyway so it doesn't matter func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithyhttp.Response) error { var errorBuffer bytes.Buffer if _, err := io.Copy(&errorBuffer, response.Body); err != nil { diff --git a/aws-protocols/internal/json/shape_deserializer.go b/aws-protocols/internal/json/shape_deserializer.go index 3a5043168..5ca6a4060 100644 --- a/aws-protocols/internal/json/shape_deserializer.go +++ b/aws-protocols/internal/json/shape_deserializer.go @@ -127,34 +127,22 @@ func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { // ReadInt8Ptr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadInt8Ptr(s *smithy.Schema, v **int8) error { - if *v == nil { - *v = new(int8) - } - return d.ReadInt8(s, *v) + return readPtr(d, s, v, d.ReadInt8) } // ReadInt16Ptr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadInt16Ptr(s *smithy.Schema, v **int16) error { - if *v == nil { - *v = new(int16) - } - return d.ReadInt16(s, *v) + return readPtr(d, s, v, d.ReadInt16) } // ReadInt32Ptr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadInt32Ptr(s *smithy.Schema, v **int32) error { - if *v == nil { - *v = new(int32) - } - return d.ReadInt32(s, *v) + return readPtr(d, s, v, d.ReadInt32) } // ReadInt64Ptr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadInt64Ptr(s *smithy.Schema, v **int64) error { - if *v == nil { - *v = new(int64) - } - return d.ReadInt64(s, *v) + return readPtr(d, s, v, d.ReadInt64) } func (d *ShapeDeserializer) readInt(min, max int64) (int64, error) { @@ -196,18 +184,12 @@ func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { // ReadFloat32Ptr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadFloat32Ptr(s *smithy.Schema, v **float32) error { - if *v == nil { - *v = new(float32) - } - return d.ReadFloat32(s, *v) + return readPtr(d, s, v, d.ReadFloat32) } // ReadFloat64Ptr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadFloat64Ptr(s *smithy.Schema, v **float64) error { - if *v == nil { - *v = new(float64) - } - return d.ReadFloat64(s, *v) + return readPtr(d, s, v, d.ReadFloat64) } func (d *ShapeDeserializer) readFloat() (float64, error) { @@ -253,10 +235,7 @@ func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { // ReadBoolPtr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadBoolPtr(s *smithy.Schema, v **bool) error { - if *v == nil { - *v = new(bool) - } - return d.ReadBool(s, *v) + return readPtr(d, s, v, d.ReadBool) } // ReadString implements [smithy.ShapeDeserializer]. @@ -280,10 +259,7 @@ func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { // ReadStringPtr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadStringPtr(s *smithy.Schema, v **string) error { - if *v == nil { - *v = new(string) - } - return d.ReadString(s, *v) + return readPtr(d, s, v, d.ReadString) } // ReadTime implements [smithy.ShapeDeserializer]. @@ -330,14 +306,15 @@ func (d *ShapeDeserializer) ReadTime(schema *smithy.Schema, v *time.Time) error // ReadTimePtr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadTimePtr(schema *smithy.Schema, v **time.Time) error { - if *v == nil { - *v = new(time.Time) - } - return d.ReadTime(schema, *v) + return readPtr(d, schema, v, d.ReadTime) } // ReadBlob implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { + if isNil, err := d.ReadNil(s); isNil || err != nil { + return err + } + tok, err := d.token() if err != nil { return err @@ -417,6 +394,10 @@ func (d *ShapeDeserializer) ReadMapKey(s *smithy.Schema) (string, bool, error) { // ReadStruct implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error { + if isNil, err := d.ReadNil(s); isNil || err != nil { + return err + } + tok, err := d.token() if err != nil { return err @@ -563,6 +544,17 @@ func (d *ShapeDeserializer) ReadDocument(schema *smithy.Schema, v *document.Valu return nil } +func readPtr[T any](d *ShapeDeserializer, s *smithy.Schema, v **T, read func(*smithy.Schema, *T) error) error { + if isNil, err := d.ReadNil(s); isNil || err != nil { + return err + } + + if *v == nil { + *v = new(T) + } + return read(s, *v) +} + func jsonToValue(v any) (document.Value, error) { switch vv := v.(type) { case nil: diff --git a/aws-protocols/internal/json/shape_serializer.go b/aws-protocols/internal/json/shape_serializer.go index 072bfff13..3e05a9879 100644 --- a/aws-protocols/internal/json/shape_serializer.go +++ b/aws-protocols/internal/json/shape_serializer.go @@ -303,6 +303,10 @@ func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { // WriteBlob implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { + if v == nil { // we don't write null so skipZeroValue is irrelevant + return + } + switch enc := s.head.Top().(type) { case *smithyjson.Object: enc.Key(s.jsonMemberName(schema)).Base64EncodeBytes(v) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java index 706e3e8ad..861e10712 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java @@ -29,6 +29,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import software.amazon.smithy.aws.traits.protocols.AwsJson1_0Trait; +import software.amazon.smithy.aws.traits.protocols.AwsJson1_1Trait; import software.amazon.smithy.aws.traits.protocols.RestJson1Trait; import software.amazon.smithy.codegen.core.CodegenException; import software.amazon.smithy.codegen.core.Symbol; @@ -272,6 +273,8 @@ private Symbol resolveDefaultProtocol() { if (preferred.equals(AwsJson1_0Trait.ID)) { return SmithyGoDependency.SMITHY_AWS_PROTOCOLS_JSON10.func("New"); + } else if (preferred.equals(AwsJson1_1Trait.ID)) { + return SmithyGoDependency.SMITHY_AWS_PROTOCOLS_JSON10.func("New11"); } else if (preferred.equals(RestJson1Trait.ID)) { return SmithyGoDependency.SMITHY_AWS_PROTOCOLS_RESTJSON1.func("New"); } else { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java index 988cc2932..f2232b177 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java @@ -7,7 +7,6 @@ import software.amazon.smithy.go.codegen.GoCodegenContext; import software.amazon.smithy.go.codegen.GoWriter; import software.amazon.smithy.go.codegen.SmithyGoDependency; -import software.amazon.smithy.go.codegen.SmithyGoTypes; import software.amazon.smithy.go.codegen.Writable; import software.amazon.smithy.go.codegen.util.ShapeUtil; import software.amazon.smithy.model.shapes.MapShape; diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java index 5fd167035..f734627b2 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java @@ -6,7 +6,6 @@ import software.amazon.smithy.go.codegen.ProtocolDocumentGenerator; import software.amazon.smithy.go.codegen.SchemaGenerator; import software.amazon.smithy.go.codegen.SmithyGoDependency; -import software.amazon.smithy.go.codegen.SmithyGoTypes; import software.amazon.smithy.go.codegen.UnsupportedShapeException; import software.amazon.smithy.go.codegen.Writable; import software.amazon.smithy.go.codegen.knowledge.GoPointableIndex; From 2e4d10c8c66c1331df85393705b3d823bd9d9aa7 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Mon, 6 Apr 2026 11:45:58 -0400 Subject: [PATCH 07/38] implement schema-serde event streams --- aws-http-auth/sigv4/stream.go | 82 +++ aws-http-auth/sigv4/stream_test.go | 81 +++ aws-protocols/awsjson10/awsjson10.go | 37 +- .../internal/httpbinding/serializer.go | 3 + aws-protocols/restjson1/restjson1.go | 38 +- .../smithy/go/codegen/CodegenVisitor.java | 44 +- .../go/codegen/EventStreamGenerator.java | 175 ++++++ .../amazon/smithy/go/codegen/GoSettings.java | 4 + .../smithy/go/codegen/OperationGenerator.java | 28 +- .../smithy/go/codegen/SchemaGenerator.java | 10 + .../smithy/go/codegen/SmithyGoDependency.java | 1 + .../smithy/go/codegen/UnionGenerator.java | 16 + .../Serde2DeserializeResponseMiddleware.java | 2 +- .../serde2/Serde2EventStreamMiddleware.java | 359 ++++++++++++ .../Serde2SerializeRequestMiddleware.java | 2 +- eventstream/codec.go | 245 ++++++++ eventstream/const.go | 24 + eventstream/debug.go | 144 +++++ eventstream/decode.go | 218 ++++++++ eventstream/decode_test.go | 188 +++++++ eventstream/deserializer.go | 364 ++++++++++++ eventstream/encode.go | 167 ++++++ eventstream/encode_test.go | 75 +++ eventstream/error.go | 23 + eventstream/header.go | 175 ++++++ eventstream/header_test.go | 66 +++ eventstream/header_value.go | 521 ++++++++++++++++++ eventstream/header_value_test.go | 203 +++++++ eventstream/message.go | 99 ++++ eventstream/serializer.go | 256 +++++++++ eventstream/shared_test.go | 152 +++++ eventstream/signer.go | 82 +++ .../decoded/negative/corrupted_header_len | 1 + .../decoded/negative/corrupted_headers | 1 + .../decoded/negative/corrupted_length | 1 + .../decoded/negative/corrupted_payload | 1 + .../testdata/decoded/positive/all_headers | 58 ++ .../testdata/decoded/positive/empty_message | 8 + .../testdata/decoded/positive/int32_header | 13 + .../decoded/positive/payload_no_headers | 8 + .../decoded/positive/payload_one_str_header | 13 + .../encoded/negative/corrupted_header_len | Bin 0 -> 61 bytes .../encoded/negative/corrupted_headers | Bin 0 -> 61 bytes .../encoded/negative/corrupted_length | Bin 0 -> 61 bytes .../encoded/negative/corrupted_payload | Bin 0 -> 29 bytes .../testdata/encoded/positive/all_headers | Bin 0 -> 204 bytes .../testdata/encoded/positive/empty_message | Bin 0 -> 16 bytes .../testdata/encoded/positive/int32_header | Bin 0 -> 45 bytes .../encoded/positive/payload_no_headers | Bin 0 -> 29 bytes .../encoded/positive/payload_one_str_header | Bin 0 -> 61 bytes schema.go | 61 +- transport/http/auth.go | 9 + transport/http/eventstream.go | 209 +++++++ transport/http/eventstream_middleware.go | 69 +++ transport/http/protocol.go | 14 +- 55 files changed, 4298 insertions(+), 52 deletions(-) create mode 100644 aws-http-auth/sigv4/stream.go create mode 100644 aws-http-auth/sigv4/stream_test.go create mode 100644 codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2EventStreamMiddleware.java create mode 100644 eventstream/codec.go create mode 100644 eventstream/const.go create mode 100644 eventstream/debug.go create mode 100644 eventstream/decode.go create mode 100644 eventstream/decode_test.go create mode 100644 eventstream/deserializer.go create mode 100644 eventstream/encode.go create mode 100644 eventstream/encode_test.go create mode 100644 eventstream/error.go create mode 100644 eventstream/header.go create mode 100644 eventstream/header_test.go create mode 100644 eventstream/header_value.go create mode 100644 eventstream/header_value_test.go create mode 100644 eventstream/message.go create mode 100644 eventstream/serializer.go create mode 100644 eventstream/shared_test.go create mode 100644 eventstream/signer.go create mode 100644 eventstream/testdata/decoded/negative/corrupted_header_len create mode 100644 eventstream/testdata/decoded/negative/corrupted_headers create mode 100644 eventstream/testdata/decoded/negative/corrupted_length create mode 100644 eventstream/testdata/decoded/negative/corrupted_payload create mode 100644 eventstream/testdata/decoded/positive/all_headers create mode 100644 eventstream/testdata/decoded/positive/empty_message create mode 100644 eventstream/testdata/decoded/positive/int32_header create mode 100644 eventstream/testdata/decoded/positive/payload_no_headers create mode 100644 eventstream/testdata/decoded/positive/payload_one_str_header create mode 100644 eventstream/testdata/encoded/negative/corrupted_header_len create mode 100644 eventstream/testdata/encoded/negative/corrupted_headers create mode 100644 eventstream/testdata/encoded/negative/corrupted_length create mode 100644 eventstream/testdata/encoded/negative/corrupted_payload create mode 100644 eventstream/testdata/encoded/positive/all_headers create mode 100644 eventstream/testdata/encoded/positive/empty_message create mode 100644 eventstream/testdata/encoded/positive/int32_header create mode 100644 eventstream/testdata/encoded/positive/payload_no_headers create mode 100644 eventstream/testdata/encoded/positive/payload_one_str_header create mode 100644 transport/http/eventstream.go create mode 100644 transport/http/eventstream_middleware.go diff --git a/aws-http-auth/sigv4/stream.go b/aws-http-auth/sigv4/stream.go new file mode 100644 index 000000000..626915a08 --- /dev/null +++ b/aws-http-auth/sigv4/stream.go @@ -0,0 +1,82 @@ +package sigv4 + +import ( + "crypto/sha256" + "encoding/hex" + "hash" + "strings" + "time" + + "github.com/aws/smithy-go/aws-http-auth/credentials" + v4internal "github.com/aws/smithy-go/aws-http-auth/internal/v4" +) + +// EventStreamSigner implements SigV4 event stream message signing. +// +// Unlike request signing, event stream message signing is **stateful**. The +// signer must be seeded with an initial signature from the originating HTTP +// request, and each message is signed using the signature of the previous +// message (starting at that seed value). Therefore the caller **MUST NOT** +// reuse an instance of an EventStreamSigner across multiple streams. +type EventStreamSigner struct { + creds credentials.Credentials + service string + region string + prev []byte +} + +// EventStreamSignerOptions is reserved for future use. +type EventStreamSignerOptions struct{} + +// NewEventStreamSigner returns an EventStreamSigner for a single stream. +func NewEventStreamSigner(creds credentials.Credentials, service, region string, seed []byte, + opts ...func(*EventStreamSignerOptions)) *EventStreamSigner { + return &EventStreamSigner{ + creds: creds, + service: service, + region: region, + prev: seed, + } +} + +// SignMessage computes the signature for the next message in the event stream. +func (s *EventStreamSigner) SignMessage(headers, payload []byte, now time.Time) ([]byte, error) { + now = now.UTC() + + scopeNow := now.Format(v4internal.ShortTimeFormat) + key := deriveKey(s.creds.SecretAccessKey, s.service, s.region, scopeNow) + scope := strings.Join([]string{ + scopeNow, + s.region, + s.service, + "aws4_request", + }, "/") + + h := sha256.New() + toSign := strings.Join([]string{ + "AWS4-HMAC-SHA256-PAYLOAD", + now.Format(v4internal.TimeFormat), + scope, + hex.EncodeToString(s.prev), + hex.EncodeToString(mkhash(h, headers)), + hex.EncodeToString(mkhash(h, payload)), + }, "\n") + + signature := hmacSHA256(key, []byte(toSign)) + s.prev = signature + + return signature, nil +} + +func deriveKey(secret, service, region, shortDate string) []byte { + key := hmacSHA256([]byte("AWS4"+secret), []byte(shortDate)) + key = hmacSHA256(key, []byte(region)) + key = hmacSHA256(key, []byte(service)) + return hmacSHA256(key, []byte("aws4_request")) +} + +func mkhash(h hash.Hash, b []byte) []byte { + h.Reset() + h.Write(b) + return h.Sum(nil) +} diff --git a/aws-http-auth/sigv4/stream_test.go b/aws-http-auth/sigv4/stream_test.go new file mode 100644 index 000000000..cf6412644 --- /dev/null +++ b/aws-http-auth/sigv4/stream_test.go @@ -0,0 +1,81 @@ +package sigv4 + +import ( + "encoding/hex" + "testing" + "time" + + "github.com/aws/smithy-go/aws-http-auth/credentials" +) + +func mustHexDecode(s string) []byte { + b, err := hex.DecodeString(s) + if err != nil { + panic(err) + } + return b +} + +func TestEventStreamSigner_SignMessage(t *testing.T) { + signer := NewEventStreamSigner( + credentials.Credentials{ + AccessKeyID: "AKID", + SecretAccessKey: "SECRET", + }, + "transcribestreaming", + "us-east-1", + []byte("foobarbazqux"), + ) + + messages := []struct { + Name string + Headers []byte + Payload []byte + Time time.Time + Expect []byte + }{ + { + Name: "first message", + Headers: []byte("foo"), + Payload: []byte("foo"), + Time: time.Unix(10, 0), + Expect: mustHexDecode("e3a136405ec1152f62136742efd41f8639cd647a1cf21fab12aedb22cccd8999"), + }, + { + Name: "second message", + Headers: []byte("bar"), + Payload: []byte("bar"), + Time: time.Unix(20, 0), + Expect: mustHexDecode("92a6ad677e839d3003672cb2cefc71ef80d42e8cb300fefce0807e8398c74068"), + }, + { + Name: "third message", + Headers: []byte("baz"), + Payload: []byte("baz"), + Time: time.Unix(30, 0), + Expect: mustHexDecode("46d6660bd40f2ff5d6246a347895cabe7f4e89d9e1f4c6dad6dabdc801923be1"), + }, + { + Name: "end of stream", + Headers: []byte{}, + Payload: []byte{}, + Time: time.Unix(30, 0), + Expect: mustHexDecode("492bcb3e21de447c85f8a9bef6c0eb833e992e405b9b3584be84edaccc5aad18"), + }, + } + + for _, tt := range messages { + t.Run(tt.Name, func(t *testing.T) { + s, err := signer.SignMessage(tt.Headers, tt.Payload, tt.Time) + if err != nil { + t.Fatal(err) + } + + expect := hex.EncodeToString(tt.Expect) + actual := hex.EncodeToString(s) + if expect != actual { + t.Errorf("%v != %v", expect, actual) + } + }) + } +} diff --git a/aws-protocols/awsjson10/awsjson10.go b/aws-protocols/awsjson10/awsjson10.go index 04a445b4a..ae0cb4ebc 100644 --- a/aws-protocols/awsjson10/awsjson10.go +++ b/aws-protocols/awsjson10/awsjson10.go @@ -10,6 +10,7 @@ import ( "github.com/aws/smithy-go" awsjson "github.com/aws/smithy-go/aws-protocols/internal/json" + "github.com/aws/smithy-go/eventstream" smithyio "github.com/aws/smithy-go/io" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" @@ -17,20 +18,34 @@ import ( // New returns an instance of the awsJson 1.0 protocol. func New() *Protocol { - return &Protocol{version: "1.0"} + return &Protocol{ + version: "1.0", + Codec: &eventstream.Codec{ + Codec: &awsjson.Codec{}, + ContentType: "application/json", + }, + } } // New11 returns an instance of the awsJson 1.1 protocol. // // TODO(serde2): figure out how we want to organize awsjson10 vs 11 func New11() *Protocol { - return &Protocol{version: "1.1"} + return &Protocol{ + version: "1.1", + Codec: &eventstream.Codec{ + Codec: &awsjson.Codec{}, + ContentType: "application/json", + }, + } } // Protocol implements aws.protocols#awsJson10. type Protocol struct { version string UseQueryCompatible bool + + *eventstream.Codec } var _ smithyhttp.ClientProtocol = (*Protocol)(nil) @@ -46,12 +61,17 @@ func (p *Protocol) ID() string { // SerializeRequest serializes a request for AWS Json 1.0. func (p *Protocol) SerializeRequest( ctx context.Context, - schema *smithy.Schema, + schema *smithy.OperationSchema, in smithy.Serializable, req *smithyhttp.Request, ) error { req.Method = http.MethodPost req.Header.Set("X-Amz-Target", fmt.Sprintf("%s.%s", middleware.GetServiceName(ctx), middleware.GetOperationName(ctx))) + if schema.IsInputEventStream() { + req.Header.Set("Content-Type", "application/vnd.amazon.eventstream") + return nil + } + req.Header.Set("Content-Type", "application/x-amz-json-"+p.version) if p.UseQueryCompatible { req.Header.Set("X-Amzn-Query-Compatible", "true") @@ -72,7 +92,7 @@ func (p *Protocol) SerializeRequest( // DeserializeResponse deserializes a response for AWS Json 1.0. func (p *Protocol) DeserializeResponse( ctx context.Context, - schema *smithy.Schema, + schema *smithy.OperationSchema, types *smithy.TypeRegistry, resp *smithyhttp.Response, out smithy.Deserializable, @@ -81,6 +101,10 @@ func (p *Protocol) DeserializeResponse( return p.deserializeError(types, resp) } + if schema.IsOutputEventStream() { + return nil + } + payload, err := io.ReadAll(resp.Body) if err != nil { return &smithy.DeserializationError{Err: err} @@ -98,6 +122,11 @@ func (p *Protocol) DeserializeResponse( return nil } +// HasInitialEventMessage is true because this is an RPC protocol. +func (*Protocol) HasInitialEventMessage() bool { + return true +} + // this handles both awsJson 1.0 and 1.1 - the only thing that 1.1 adds is // error shape renaming (basically not having the namespace) but both versions // of the protocol are supposed to support this anyway so it doesn't matter diff --git a/aws-protocols/internal/httpbinding/serializer.go b/aws-protocols/internal/httpbinding/serializer.go index 7886b4dcb..fd31c49e1 100644 --- a/aws-protocols/internal/httpbinding/serializer.go +++ b/aws-protocols/internal/httpbinding/serializer.go @@ -287,6 +287,9 @@ func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { } bt, bn := s.resolveBinding(schema) + if v == "" && !s.options.WriteZeroValues && bt != bindLabel { + return + } switch bt { case bindHeaderList: escaped := v diff --git a/aws-protocols/restjson1/restjson1.go b/aws-protocols/restjson1/restjson1.go index 0a6a98450..558c267dd 100644 --- a/aws-protocols/restjson1/restjson1.go +++ b/aws-protocols/restjson1/restjson1.go @@ -10,17 +10,25 @@ import ( "github.com/aws/smithy-go" internalhttpbinding "github.com/aws/smithy-go/aws-protocols/internal/httpbinding" internaljson "github.com/aws/smithy-go/aws-protocols/internal/json" + "github.com/aws/smithy-go/eventstream" smithyio "github.com/aws/smithy-go/io" smithyhttp "github.com/aws/smithy-go/transport/http" ) // New returns an instance of the aws.protocols#restJson1 protocol. func New() *Protocol { - return &Protocol{} + return &Protocol{ + Codec: &eventstream.Codec{ + Codec: &internaljson.Codec{}, + ContentType: "application/json", + }, + } } // Protocol implements aws.protocols#restJson1. -type Protocol struct{} +type Protocol struct { + *eventstream.Codec +} var _ smithyhttp.ClientProtocol = (*Protocol)(nil) @@ -32,17 +40,22 @@ func (*Protocol) ID() string { // SerializeRequest serializes a request for restJson1. func (p *Protocol) SerializeRequest( ctx context.Context, - op *smithy.Schema, + op *smithy.OperationSchema, in smithy.Serializable, req *smithyhttp.Request, ) error { - serializer, err := internalhttpbinding.NewShapeSerializer(op, req, internaljson.NewShapeSerializer(sUseJSONName)) + serializer, err := internalhttpbinding.NewShapeSerializer(op.Schema, req, internaljson.NewShapeSerializer(sUseJSONName)) if err != nil { return err } in.Serialize(serializer) - if err := serializer.Build(in, "application/json"); err != nil { + + contentType := "application/json" + if op.IsInputEventStream() { + contentType = "application/vnd.amazon.eventstream" + } + if err := serializer.Build(in, contentType); err != nil { return fmt.Errorf("build request: %w", err) } @@ -52,7 +65,7 @@ func (p *Protocol) SerializeRequest( // DeserializeResponse deserializes a response for restJson1. func (p *Protocol) DeserializeResponse( ctx context.Context, - op *smithy.Schema, + op *smithy.OperationSchema, types *smithy.TypeRegistry, resp *smithyhttp.Response, out smithy.Deserializable, @@ -72,6 +85,14 @@ func (p *Protocol) DeserializeResponse( return nil } + if op.IsOutputEventStream() { + deser := internalhttpbinding.NewShapeDeserializer(resp.Response, bd(nil), nil) + if err := out.Deserialize(deser); err != nil { + return &smithy.DeserializationError{Err: err} + } + return nil + } + payload, err := io.ReadAll(resp.Body) if err != nil { return &smithy.DeserializationError{Err: err} @@ -85,6 +106,11 @@ func (p *Protocol) DeserializeResponse( return nil } +// HasInitialEventMessage is false for REST-style protocols with HTTP bindings. +func (*Protocol) HasInitialEventMessage() bool { + return false +} + func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithyhttp.Response) error { var errorBuffer bytes.Buffer if _, err := io.Copy(&errorBuffer, response.Body); err != nil { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java index 0bcd05a65..9eef82873 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java @@ -40,11 +40,13 @@ import software.amazon.smithy.go.codegen.serde2.MapDeserializer; import software.amazon.smithy.go.codegen.serde2.MapSerializer; import software.amazon.smithy.go.codegen.serde2.Serde2DeserializeResponseMiddleware; +import software.amazon.smithy.go.codegen.serde2.Serde2EventStreamMiddleware; import software.amazon.smithy.go.codegen.serde2.Serde2SerializeRequestMiddleware; import software.amazon.smithy.go.codegen.serde2.UnionDeserializer; import software.amazon.smithy.go.codegen.serde2.UnionSerializer; import software.amazon.smithy.go.codegen.util.ShapeUtil; import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.knowledge.EventStreamIndex; import software.amazon.smithy.model.knowledge.ServiceIndex; import software.amazon.smithy.model.knowledge.TopDownIndex; import software.amazon.smithy.model.neighbor.Walker; @@ -269,36 +271,6 @@ void execute() { protocolDocumentGenerator.generateInternalDocumentTypes(protocolGenerator, contextBuilder.build()); } - // TODO(serde2): event streams still need all the old stuff as we're migrating over to schema-serde, once we've - // done that for event streams remove this - if (settings.useExperimentalSerde() && protocolGenerator != null && eventStreamGenerator.hasEventStreamOperations()) { - ProtocolGenerator.GenerationContext.Builder contextBuilder = ProtocolGenerator.GenerationContext.builder() - .protocolName(protocolGenerator.getProtocolName()) - .integrations(integrations) - .model(model) - .service(service) - .settings(settings) - .symbolProvider(symbolProvider) - .delegator(writers); - - writers.useFileWriter("serializers.go", settings.getModuleName(), writer -> { - ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); - protocolGenerator.generateRequestSerializers(context); - protocolGenerator.generateSharedSerializerComponents(context); - }); - - writers.useFileWriter("deserializers.go", settings.getModuleName(), writer -> { - ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); - protocolGenerator.generateResponseDeserializers(context); - protocolGenerator.generateSharedDeserializerComponents(context); - }); - - eventStreamGenerator.writeEventStreamImplementation(writer -> { - ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); - protocolGenerator.generateEventStreamComponents(context); - }); - } - LOGGER.info("Generating protocol tests for " + service.getId()); ProtocolUtils.generateHttpProtocolTests(ctx); @@ -368,6 +340,18 @@ void execute() { Writable.map(maps, it -> new MapDeserializer(ctx, it), true)); ctx.writerDelegator().useFileWriter("types/common_serde.go", settings.getModuleName() + "/types", Writable.map(maps, it -> new MapDeserializer(ctx, it), true)); + + if (eventStreamGenerator.hasEventStreamOperations()) { + var streamIndex = EventStreamIndex.of(model); + eventStreamGenerator.writeEventStreamImplementation(writer -> { + writer.addImport(settings.getModuleName() + "/schemas", "schemas"); + TopDownIndex.of(model).getContainedOperations(service).stream() + .filter(op -> streamIndex.getInputInfo(op).isPresent() + || streamIndex.getOutputInfo(op).isPresent()) + .sorted() + .forEach(op -> writer.write(new Serde2EventStreamMiddleware(ctx, op))); + }); + } } // This is all stuff that we'll still need to do but it DEPENDS on ProtocolGenerator right now diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/EventStreamGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/EventStreamGenerator.java index 974ec6a9b..f482942a7 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/EventStreamGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/EventStreamGenerator.java @@ -15,9 +15,13 @@ package software.amazon.smithy.go.codegen; +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; + +import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.function.Consumer; +import java.util.stream.Collectors; import java.util.stream.Stream; import software.amazon.smithy.codegen.core.Symbol; @@ -26,13 +30,17 @@ import software.amazon.smithy.model.knowledge.EventStreamIndex; import software.amazon.smithy.model.knowledge.OperationIndex; import software.amazon.smithy.model.knowledge.TopDownIndex; +import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.OperationShape; import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.shapes.StructureShape; import software.amazon.smithy.model.shapes.ToShapeId; +import software.amazon.smithy.model.shapes.UnionShape; +import software.amazon.smithy.model.traits.ErrorTrait; import software.amazon.smithy.model.traits.StreamingTrait; +import software.amazon.smithy.utils.MapUtils; import software.amazon.smithy.utils.StringUtils; public final class EventStreamGenerator { @@ -131,9 +139,164 @@ public void generateEventStreamInterfaces() { writer.write("Err() error"); }); }); + + if (settings.useExperimentalSerde()) { + inputEvents.forEach(shapeId -> { + var union = model.expectShape(shapeId, UnionShape.class); + generateWriterAdapter(writer, union); + }); + outputEvents.forEach(shapeId -> { + var union = model.expectShape(shapeId, UnionShape.class); + generateReaderAdapter(writer, union); + }); + } }); } + private void generateWriterAdapter(GoWriter writer, UnionShape union) { + var unionSymbol = symbolProvider.toSymbol(union); + var implName = StringUtils.uncapitalize(union.getId().getName(serviceShape)) + "Writer"; + var ifaceName = getEventStreamWriterInterfaceName(serviceShape, union); + var members = union.members().stream() + .filter(m -> !m.getMemberTrait(model, ErrorTrait.class).isPresent()) + .sorted() + .toList(); + + writer.addImport(settings.getModuleName() + "/schemas", "schemas"); + writer.writeGoTemplate(""" + type $impl:L struct { + writer $esWriter:P + } + + var _ $iface:L = (*$impl:L)(nil) + + func (w *$impl:L) Send(ctx $context:T, event $union:T) error { + var variant $schema:P + switch event.(type) { + $cases:W + default: + return $fmtErrorf:T("unknown event type: %T", event) + } + sv, ok := event.($serializable:T) + if !ok { + return $fmtErrorf:T("event %T is not serializable", event) + } + return w.writer.Send(ctx, variant, sv) + } + + func (w *$impl:L) Close() error { + return w.writer.Close() + } + + func (w *$impl:L) Err() error { + return w.writer.Err() + } + """, + MapUtils.of( + "impl", implName, + "iface", ifaceName, + "esWriter", SmithyGoDependency.SMITHY_HTTP_TRANSPORT.pointableSymbol("EventStreamWriter"), + "context", SymbolUtils.createValueSymbolBuilder("Context", SmithyGoDependency.CONTEXT).build(), + "union", unionSymbol, + "schema", SmithyGoDependency.SMITHY.pointableSymbol("Schema"), + "serializable", SmithyGoDependency.SMITHY.valueSymbol("Serializable"), + "fmtErrorf", SymbolUtils.createValueSymbolBuilder("Errorf", SmithyGoDependency.FMT).build(), + "cases", (Writable) (GoWriter w) -> { + for (var member : members) { + var variantSymbol = SymbolUtils.createPointableSymbolBuilder( + symbolProvider.toMemberName(member), + unionSymbol.getNamespace()).build(); + var schemaName = SchemaGenerator.getMemberSchemaName(union, member, serviceShape); + w.write("case $P:", variantSymbol); + w.write(" variant = schemas.$L", schemaName); + } + } + )); + } + + private void generateReaderAdapter(GoWriter writer, UnionShape union) { + var unionSymbol = symbolProvider.toSymbol(union); + var implName = StringUtils.uncapitalize(union.getId().getName(serviceShape)) + "Reader"; + var ifaceName = getEventStreamReaderInterfaceName(serviceShape, union); + var members = union.members().stream() + .filter(m -> !m.getMemberTrait(model, ErrorTrait.class).isPresent()) + .sorted() + .toList(); + + writer.writeGoTemplate(""" + type $impl:L struct { + reader $esReader:P + ch chan $union:T + done chan struct{} + } + + var _ $iface:L = (*$impl:L)(nil) + + func $newImpl:L(reader $esReader:P) *$impl:L { + r := &$impl:L{ + reader: reader, + ch: make(chan $union:T), + done: make(chan struct{}), + } + go r.pipe() + return r + } + + func (r *$impl:L) pipe() { + defer close(r.ch) + for event := range r.reader.Events() { + var ev $union:T + switch v := event.(type) { + $cases:W + default: + continue + } + select { + case r.ch <- ev: + case <-r.done: + return + } + } + } + + func (r *$impl:L) Events() <-chan $union:T { + return r.ch + } + + func (r *$impl:L) Close() error { + close(r.done) + return r.reader.Close() + } + + func (r *$impl:L) Err() error { + return r.reader.Err() + } + """, + MapUtils.of( + "impl", implName, + "iface", ifaceName, + "newImpl", "new" + StringUtils.capitalize(implName), + "esReader", SmithyGoDependency.SMITHY_HTTP_TRANSPORT.pointableSymbol("EventStreamReader"), + "union", unionSymbol, + "cases", (Writable) (GoWriter w) -> { + for (var member : members) { + var targetSymbol = symbolProvider.toSymbol(model.expectShape(member.getTarget())); + var variantSymbol = SymbolUtils.createPointableSymbolBuilder( + symbolProvider.toMemberName(member), + unionSymbol.getNamespace()).build(); + w.write("case $P:", targetSymbol); + w.write(" ev = &$T{Value: *v}", variantSymbol); + } + var esUnknown = SmithyGoDependency.SMITHY_EVENTSTREAM + .pointableSymbol("UnknownUnionMember"); + var typesUnknown = SymbolUtils.createPointableSymbolBuilder( + "UnknownUnionMember", unionSymbol.getNamespace()).build(); + w.write("case $P:", esUnknown); + w.write(" ev = &$T{Tag: v.Tag, Value: v.Value}", typesUnknown); + } + )); + } + public boolean hasEventStreamOperations() { return hasEventStreamOperations(model, serviceShape, streamIndex); } @@ -469,4 +632,16 @@ public static String getEventStreamReaderInterfaceName(ServiceShape serviceShape String name = StringUtils.capitalize(shape.toShapeId().getName(serviceShape)); return name + "Reader"; } + + public static String getEventStreamWriterAdapterName(ServiceShape serviceShape, ToShapeId shape) { + return StringUtils.uncapitalize(shape.toShapeId().getName(serviceShape)) + "Writer"; + } + + public static String getEventStreamReaderAdapterName(ServiceShape serviceShape, ToShapeId shape) { + return StringUtils.uncapitalize(shape.toShapeId().getName(serviceShape)) + "Reader"; + } + + public static String getEventStreamReaderAdapterConstructor(ServiceShape serviceShape, ToShapeId shape) { + return "new" + StringUtils.capitalize(shape.toShapeId().getName(serviceShape)) + "Reader"; + } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java index d68af2440..7059bc5f8 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java @@ -58,6 +58,10 @@ public final class GoSettings { private static final String USE_EXPERIMENTAL_SERDE = "useExperimentalSerde"; private static final String GO_DIRECTIVE = "goDirective"; + // TODO(serde2): add useLegacyUnknownMessageError setting for SDK backwards compat: + // 1. generates per-service UnknownEventMessageError type (with Message being the old SDK *eventstream.Message) + // 2. generates extra code in the reader adapter to map smithyeventstream.UnknownMessageError to .UnknownMessageError + private ShapeId service; private String moduleName; private String moduleDescription = ""; diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java index b36857325..4785c0cb5 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java @@ -27,6 +27,7 @@ import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin; import software.amazon.smithy.model.Model; +import software.amazon.smithy.model.knowledge.EventStreamIndex; import software.amazon.smithy.model.knowledge.OperationIndex; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.OperationShape; @@ -291,14 +292,37 @@ private void generateOperationProtocolMiddlewareAdders() { } else { writer.addUseImports(SmithyGoDependency.SMITHY); var opSchemaName = "schemas." + SchemaGenerator.getSchemaName(operation, service); + var inputSchemaName = "schemas." + SchemaGenerator.getSchemaName(input, service); + var outputSchemaName = "schemas." + SchemaGenerator.getSchemaName(output, service); + var opSchema = String.format("smithy.NewOperationSchema(%s, %s, %s)", + opSchemaName, inputSchemaName, outputSchemaName); writer.write(""" if err := stack.Serialize.Add(&serializeRequestMiddleware{options: &options, operationSchema: $L}, middleware.After); err != nil { return err - }""", opSchemaName); + }""", opSchema); writer.write(""" if err := stack.Deserialize.Add(&deserializeResponseMiddleware{options: &options, operationSchema: $L, output: &$T{}}, middleware.After); err != nil { return err - }""", opSchemaName, symbolProvider.toSymbol(output)); + }""", opSchema, symbolProvider.toSymbol(output)); + + if (Stream.concat(input.members().stream(), output.members().stream()) + .anyMatch(m -> StreamingTrait.isEventStream(model, m))) { + + var isInput = EventStreamIndex.of(model).getInputInfo(operation).isPresent(); + if (isInput) { + writer.addUseImports(SmithyGoDependency.SMITHY_HTTP_TRANSPORT); + writer.write(""" + if err := smithyhttp.AddInitializeStreamWriter(stack); err != nil { + return err + }"""); + } + + var name = String.format("deserializeOpEventStream%s", operation.getId().getName(service)); + writer.write(""" + if err := stack.Deserialize.Insert(&$L{options: &options}, "OperationDeserializer", middleware.Before); err != nil { + return err + }""", name); + } } // FUTURE: retry middleware should be at the front of finalize, right now it's added by the SDK diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java index 6941cda6b..1357f027c 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java @@ -22,8 +22,18 @@ public SchemaGenerator(GoCodegenContext ctx, Shape shape) { this.shape = shape; } + // TODO(serde2): synthetic shapes (smithy.go.synthetic namespace) can + // collide with modeled shapes that happen to have the same name (e.g. + // ConverseOutput is both a modeled union and a synthetic operation output + // in bedrockruntime). For now just throw a prefix on there so it doesn't + // conflict but I don't like that. + private static final String SYNTHETIC_NAMESPACE = "smithy.go.synthetic"; + public static String getSchemaName(Shape shape, ServiceShape service) { var name = shape.getId().getName(service); + if (shape.getId().getNamespace().equals(SYNTHETIC_NAMESPACE)) { + name = "SmithyGoSynthetic_" + name; + } return ShapeUtil.isExported(shape) ? name : "_" + name; diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java index d42c10437..d31f0c42c 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java @@ -73,6 +73,7 @@ public final class SmithyGoDependency { public static final GoDependency SMITHY_DOCUMENT_JSON = smithy("document/json", "smithydocumentjson"); public static final GoDependency SMITHY_DOCUMENT_CBOR = smithy("document/cbor", "smithydocumentcbor"); public static final GoDependency SMITHY_SYNC = smithy("sync", "smithysync"); + public static final GoDependency SMITHY_EVENTSTREAM = smithy("eventstream", "eventstream"); public static final GoDependency SMITHY_AUTH = smithy("auth", "smithyauth"); public static final GoDependency SMITHY_AUTH_BEARER = smithy("auth/bearer"); public static final GoDependency SMITHY_ENDPOINTS = smithy("endpoints", "smithyendpoints"); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java index 395ade1cb..01b55e6a0 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java @@ -132,6 +132,10 @@ private void generateMemberSerializer(GoWriter writer, MemberShape member, Strin case BIG_DECIMAL -> writer.write("s.WriteBigDecimal($L, v.Value)", schemaName); case STRUCTURE -> writer.write("s.WriteStruct($L, &v.Value)", schemaName); // struct variants are value types case LIST, SET, MAP -> writer.write("serialize$L(s, $L, v.Value)", target.getId().getName(), schemaName); + case DOCUMENT -> { + writer.addUseImports(SmithyGoDependency.SMITHY_DOCUMENT); + writer.write("s.WriteDocument($L, &smithydocument.Opaque{Value: v.Value})", schemaName); + } case MEMBER, SERVICE, RESOURCE, OPERATION -> throw new CodegenException("invalid shape type " + target.getType()); } }); @@ -167,6 +171,18 @@ private void generateMemberDeserializer(GoWriter writer, MemberShape member, Str } case STRUCTURE -> writer.write("return v.Value.Deserialize(d)"); case LIST, MAP, UNION -> writer.write("return deserialize$L(d, $L, &v.Value)", target.getId().getName(), schemaName); + case DOCUMENT -> { + var unmarshaler = ProtocolDocumentGenerator.Utilities.getInternalDocumentSymbolBuilder( + ctx.settings(), ProtocolDocumentGenerator.INTERNAL_NEW_DOCUMENT_UNMARSHALER_FUNC).build(); + writer.addUseImports(SmithyGoDependency.SMITHY_DOCUMENT); + writer.write(""" + var dv smithydocument.Value + if err := d.ReadDocument($1L, &dv); err != nil { return err } + if ov, ok := dv.(smithydocument.Opaque); ok { + v.Value = $2T(ov.Value) + } + return nil""", schemaName, unmarshaler); + } case MEMBER, SERVICE, RESOURCE, OPERATION -> throw new CodegenException("invalid shape type " + target.getType()); } }); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2DeserializeResponseMiddleware.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2DeserializeResponseMiddleware.java index f8f6019eb..a612a04c8 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2DeserializeResponseMiddleware.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2DeserializeResponseMiddleware.java @@ -25,7 +25,7 @@ public String getId() { public Map getFields() { return Map.of( "options", pointerTo(buildPackageSymbol("Options")), - "operationSchema", SmithyGoDependency.SMITHY.pointableSymbol("Schema"), + "operationSchema", SmithyGoDependency.SMITHY.pointableSymbol("OperationSchema"), "output", SmithyGoDependency.SMITHY.interfaceSymbol("Deserializable") ); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2EventStreamMiddleware.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2EventStreamMiddleware.java new file mode 100644 index 000000000..9067d14fb --- /dev/null +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2EventStreamMiddleware.java @@ -0,0 +1,359 @@ +package software.amazon.smithy.go.codegen.serde2; + +import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; +import static software.amazon.smithy.go.codegen.SymbolUtils.buildPackageSymbol; +import static software.amazon.smithy.go.codegen.SymbolUtils.pointerTo; + +import java.util.Map; +import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.go.codegen.EventStreamGenerator; +import software.amazon.smithy.go.codegen.GoCodegenContext; +import software.amazon.smithy.go.codegen.SchemaGenerator; +import software.amazon.smithy.go.codegen.SmithyGoDependency; +import software.amazon.smithy.go.codegen.Writable; +import software.amazon.smithy.go.codegen.middleware.DeserializeStepMiddleware; +import software.amazon.smithy.model.knowledge.EventStreamIndex; +import software.amazon.smithy.model.shapes.OperationShape; +import software.amazon.smithy.model.shapes.UnionShape; +import software.amazon.smithy.utils.MapUtils; + +public class Serde2EventStreamMiddleware extends DeserializeStepMiddleware { + private final GoCodegenContext ctx; + private final OperationShape operation; + + public Serde2EventStreamMiddleware(GoCodegenContext ctx, OperationShape operation) { + this.ctx = ctx; + this.operation = operation; + } + + @Override + public String getStructName() { + return String.format("deserializeOpEventStream%s", + operation.getId().getName(ctx.service())); + } + + @Override + public String getId() { + return "OperationEventStreamDeserializer"; + } + + @Override + public Map getFields() { + return Map.of( + "options", pointerTo(buildPackageSymbol("Options")) + ); + } + + @Override + public Writable getFuncBody() { + var model = ctx.model(); + var service = ctx.service(); + var symbolProvider = ctx.symbolProvider(); + var streamIndex = EventStreamIndex.of(model); + + var inputInfo = streamIndex.getInputInfo(operation); + var outputInfo = streamIndex.getOutputInfo(operation); + + var outputShape = model.expectShape(operation.getOutputShape()); + var outputSymbol = symbolProvider.toSymbol(outputShape); + + var opEventStreamConstructor = EventStreamGenerator + .getEventStreamOperationStructureConstructor(service, operation); + var opEventStreamSymbol = EventStreamGenerator + .getEventStreamOperationStructureSymbol(service, operation); + + var isV2 = EventStreamGenerator.isV2EventStream(model, operation); + + if (isV2) { + return v2FuncBody(inputInfo, outputInfo, outputSymbol, opEventStreamConstructor, opEventStreamSymbol); + } + + return goTemplate(""" + out, md, err := next.HandleDeserialize(ctx, in) + if err != nil { + return out, md, err + } + + $respDecl:L, ok := out.RawResponse.($smithyhttpResponse:P) + if !ok { + return out, md, $fmtErrorf:T("unknown transport type: %T", out.RawResponse) + } + + output, ok := out.Result.($output:P) + if out.Result != nil && !ok { + return out, md, $fmtErrorf:T("unexpected output result type %T, expected $output:P", out.Result) + } else if out.Result == nil { + output = &$output:T{} + out.Result = output + } + + $writerSetup:W + $initialRequest:W + $readerSetup:W + $initialResponse:W + + output.eventStream = $esConstructor:T(func(stream $esStruct:P) { + $wireWriter:W + $wireReader:W + }) + + go output.eventStream.waitStreamClose() + + return out, md, nil + """, + Map.ofEntries( + Map.entry("smithyhttpResponse", + SmithyGoDependency.SMITHY_HTTP_TRANSPORT.pointableSymbol("Response")), + Map.entry("fmtErrorf", SmithyGoDependency.FMT.func("Errorf")), + Map.entry("output", outputSymbol), + Map.entry("respDecl", outputInfo.isPresent() ? "resp" : "_"), + Map.entry("esConstructor", opEventStreamConstructor), + Map.entry("esStruct", opEventStreamSymbol), + Map.entry("writerSetup", inputInfo.isPresent() + ? writerSetup(inputInfo.get().getEventStreamTarget().asUnionShape().get()) + : (Writable) w -> {}), + Map.entry("initialRequest", inputInfo.isPresent() + ? initialRequest() + : (Writable) w -> {}), + Map.entry("readerSetup", outputInfo.isPresent() + ? readerSetup(outputInfo.get().getEventStreamTarget().asUnionShape().get()) + : (Writable) w -> {}), + Map.entry("initialResponse", outputInfo.isPresent() + ? initialResponse(outputShape) + : (Writable) w -> {}), + Map.entry("wireWriter", inputInfo.isPresent() + ? (Writable) w -> w.write("stream.Writer = eventWriter") + : (Writable) w -> {}), + Map.entry("wireReader", outputInfo.isPresent() + ? (Writable) w -> w.write("stream.Reader = eventReader") + : (Writable) w -> {}) + )); + } + + private Writable writerSetup(UnionShape inputEventStream) { + var service = ctx.service(); + var schemaName = "schemas." + SchemaGenerator.getSchemaName(inputEventStream, service); + var adapterName = EventStreamGenerator.getEventStreamWriterAdapterName(service, inputEventStream); + + return goTemplate(""" + inputStreamWriter := $getInputStreamWriter:T(ctx) + if inputStreamWriter == nil { + return out, md, $fmtErrorf:T("input stream writer not found in context") + } + if rscheme := getResolvedAuthScheme(ctx); rscheme != nil { + if es, ok := rscheme.Scheme.Signer().($eventStreamSigner:T); ok { + req, _ := in.Request.($smithyhttpRequest:P) + msgSigner, serr := es.NewMessageSigner(ctx, req, getIdentity(ctx), rscheme.SignerProperties) + if serr != nil { + return out, md, $fmtErrorf:T("event stream signer: %w", serr) + } + inputStreamWriter = $newSigningWriter:T(inputStreamWriter, msgSigner) + } + } + eventWriter := &$adapter:L{ + writer: $newWriter:T(m.options.Protocol, $schema:L, inputStreamWriter), + } + defer func() { + if err != nil { + _ = eventWriter.Close() + } + }() + """, + MapUtils.of( + "getInputStreamWriter", + SmithyGoDependency.SMITHY_HTTP_TRANSPORT.func("GetInputStreamWriter"), + "fmtErrorf", SmithyGoDependency.FMT.func("Errorf"), + "adapter", adapterName, + "newWriter", + SmithyGoDependency.SMITHY_HTTP_TRANSPORT.func("NewEventStreamWriter"), + "eventStreamSigner", + SmithyGoDependency.SMITHY_HTTP_TRANSPORT.valueSymbol("EventStreamSigner"), + "smithyhttpRequest", + SmithyGoDependency.SMITHY_HTTP_TRANSPORT.pointableSymbol("Request"), + "newSigningWriter", + SmithyGoDependency.SMITHY_EVENTSTREAM.func("NewSigningWriter"), + "schema", schemaName + )); + } + + private Writable readerSetup(UnionShape outputEventStream) { + var service = ctx.service(); + var schemaName = "schemas." + SchemaGenerator.getSchemaName(outputEventStream, service); + var adapterConstructor = EventStreamGenerator + .getEventStreamReaderAdapterConstructor(service, outputEventStream); + + return goTemplate(""" + eventReader := $newAdapter:L( + $newReader:T(m.options.Protocol, $schema:L, TypeRegistry, resp.Body), + ) + defer func() { + if err != nil { + _ = eventReader.Close() + } + }() + """, + MapUtils.of( + "newAdapter", adapterConstructor, + "newReader", + SmithyGoDependency.SMITHY_HTTP_TRANSPORT.func("NewEventStreamReader"), + "schema", schemaName + )); + } + + private Writable initialRequest() { + var inputShape = ctx.model().expectShape(operation.getInputShape()); + var inputSchemaName = "schemas." + SchemaGenerator.getSchemaName(inputShape, ctx.service()); + + return goTemplate(""" + if m.options.Protocol.HasInitialEventMessage() { + opInput, ok := getOperationInput(ctx).($serializable:T) + if !ok { + return out, md, $fmtErrorf:T("operation input is not serializable") + } + if err = m.options.Protocol.SerializeInitialRequest($inputSchema:L, opInput, inputStreamWriter); err != nil { + return out, md, $fmtErrorf:T("serialize initial request: %w", err) + } + } + """, + MapUtils.of( + "serializable", SmithyGoDependency.SMITHY.valueSymbol("Serializable"), + "fmtErrorf", SmithyGoDependency.FMT.func("Errorf"), + "inputSchema", inputSchemaName + )); + } + + private Writable initialResponse(software.amazon.smithy.model.shapes.Shape outputShape) { + var outputSchemaName = "schemas." + SchemaGenerator.getSchemaName(outputShape, ctx.service()); + + return goTemplate(""" + if m.options.Protocol.HasInitialEventMessage() { + if err = m.options.Protocol.DeserializeInitialResponse($outputSchema:L, resp.Body, output); err != nil { + return out, md, $fmtErrorf:T("deserialize initial response: %w", err) + } + } + """, + MapUtils.of( + "fmtErrorf", SmithyGoDependency.FMT.func("Errorf"), + "outputSchema", outputSchemaName + )); + } + + private Writable v2FuncBody( + java.util.Optional inputInfo, + java.util.Optional outputInfo, + Symbol outputSymbol, + Symbol opEventStreamConstructor, + Symbol opEventStreamSymbol) { + + // For v2 (early-return) event streams, we must: + // 1. Set up the writer (from the pipe in context) + // 2. Set up the reader using an async pipe (not the response body directly) + // 3. Wire up the event stream and send PartialResult to unblock the caller + // 4. THEN call next.HandleDeserialize (sends the HTTP request) + // 5. Pipe the response body into the async reader + // 6. Add output to metadata for the Build-step middleware + return goTemplate(""" + var out $deserializeOutput:T + var md $metadata:T + var err error + + output := &$output:T{} + output.initialReply = make(chan $initialReply:T, 1) + + $writerSetup:W + $readerSetup:W + + output.eventStream = $esConstructor:T(func(stream $esStruct:P) { + $wireWriter:W + $wireReader:W + }) + + go output.eventStream.waitStreamClose() + + prc, _ := ctx.Value(partialResultChan{}).(chan PartialResult) + if prc != nil { + select { + case <-prc: + default: + } + prc <- PartialResult{ + Output: output, + Metadata: $newMetadata:T{}, + } + } + + out, md, err = next.HandleDeserialize(ctx, in) + if err != nil { + $asyncError:W + return out, md, err + } + + resp, ok := out.RawResponse.($smithyhttpResponse:P) + if !ok { + return out, md, $fmtErrorf:T("unknown transport type: %T", out.RawResponse) + } + + $asyncPipe:W + $addToMetadata:T(&md, output) + return out, md, nil + """, + Map.ofEntries( + Map.entry("output", outputSymbol), + Map.entry("deserializeOutput", + SmithyGoDependency.SMITHY_MIDDLEWARE.valueSymbol("DeserializeOutput")), + Map.entry("metadata", + SmithyGoDependency.SMITHY_MIDDLEWARE.valueSymbol("Metadata")), + Map.entry("initialReply", + EventStreamGenerator.getEventStreamInitialReplyStructureSymbol( + ctx.service(), operation)), + Map.entry("smithyhttpResponse", + SmithyGoDependency.SMITHY_HTTP_TRANSPORT.pointableSymbol("Response")), + Map.entry("fmtErrorf", SmithyGoDependency.FMT.func("Errorf")), + Map.entry("esConstructor", opEventStreamConstructor), + Map.entry("esStruct", opEventStreamSymbol), + Map.entry("newMetadata", + SmithyGoDependency.SMITHY_MIDDLEWARE.valueSymbol("Metadata")), + Map.entry("addToMetadata", + SmithyGoDependency.SMITHY_MIDDLEWARE.func("AddEventStreamOutputToMetadata")), + Map.entry("writerSetup", inputInfo.isPresent() + ? writerSetup(inputInfo.get().getEventStreamTarget().asUnionShape().get()) + : (Writable) w -> {}), + Map.entry("wireWriter", inputInfo.isPresent() + ? (Writable) w -> w.write("stream.Writer = eventWriter") + : (Writable) w -> {}), + Map.entry("readerSetup", outputInfo.isPresent() + ? v2ReaderSetup(outputInfo.get().getEventStreamTarget().asUnionShape().get()) + : (Writable) w -> {}), + Map.entry("wireReader", outputInfo.isPresent() + ? (Writable) w -> w.write("stream.Reader = eventReader") + : (Writable) w -> {}), + Map.entry("asyncError", outputInfo.isPresent() + ? (Writable) w -> w.write("asyncResult <- deserializeResult{err: err}") + : (Writable) w -> {}), + Map.entry("asyncPipe", outputInfo.isPresent() + ? (Writable) w -> w.write("asyncResult <- deserializeResult{reader: resp.Body}") + : (Writable) w -> {}) + )); + } + + private Writable v2ReaderSetup(UnionShape outputEventStream) { + var service = ctx.service(); + var schemaName = "schemas." + SchemaGenerator.getSchemaName(outputEventStream, service); + var adapterConstructor = EventStreamGenerator + .getEventStreamReaderAdapterConstructor(service, outputEventStream); + + return goTemplate(""" + asyncResult := make(chan deserializeResult, 1) + asyncReader := newAsyncEventStreamReader(asyncResult) + eventReader := $newAdapter:L( + $newReader:T(m.options.Protocol, $schema:L, TypeRegistry, asyncReader.pipeReader), + ) + """, + MapUtils.of( + "newAdapter", adapterConstructor, + "newReader", + SmithyGoDependency.SMITHY_HTTP_TRANSPORT.func("NewEventStreamReader"), + "schema", schemaName + )); + } +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2SerializeRequestMiddleware.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2SerializeRequestMiddleware.java index e85533988..766120a1b 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2SerializeRequestMiddleware.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2SerializeRequestMiddleware.java @@ -26,7 +26,7 @@ public String getId() { public Map getFields() { return Map.of( "options", pointerTo(buildPackageSymbol("Options")), - "operationSchema", SmithyGoDependency.SMITHY.pointableSymbol("Schema") + "operationSchema", SmithyGoDependency.SMITHY.pointableSymbol("OperationSchema") ); } diff --git a/eventstream/codec.go b/eventstream/codec.go new file mode 100644 index 000000000..548e6f9ca --- /dev/null +++ b/eventstream/codec.go @@ -0,0 +1,245 @@ +package eventstream + +import ( + "bytes" + "fmt" + "io" + + "github.com/aws/smithy-go" +) + +// Codec orchestrates event stream message serde for protocols that use the +// standard event stream binary framing. All existing AWS protocols (and the +// Smithy rpcv2Cbor protocol) use this framing. +type Codec struct { + Codec smithy.Codec + ContentType string + + encoder *Encoder + decoder *Decoder + payloadBuf []byte +} + +func (c *Codec) enc() *Encoder { + if c.encoder == nil { + c.encoder = NewEncoder() + } + return c.encoder +} + +func (c *Codec) dec() *Decoder { + if c.decoder == nil { + c.decoder = NewDecoder() + } + return c.decoder +} + +// SerializeEventMessage serializes an event to the input stream. +func (c *Codec) SerializeEventMessage(schema, variant *smithy.Schema, v smithy.Serializable, w io.Writer) error { + var msg Message + + inner := c.Codec.Serializer() + ss := NewShapeSerializer(&msg, inner) + + v.Serialize(ss) + + msg.Headers.Set(MessageTypeHeader, StringValue(EventMessageType)) + msg.Headers.Set(EventTypeHeader, StringValue(variant.MemberName())) + + if ct := ss.ContentType(); ct != "" { + msg.Headers.Set(ContentTypeHeader, StringValue(ct)) + } else if payload := inner.Bytes(); len(payload) > 0 { + msg.Payload = payload + msg.Headers.Set(ContentTypeHeader, StringValue(c.ContentType)) + } + + return c.enc().Encode(w, msg) +} + +// DeserializeEventMessage reads an event from the output stream. Returns +// io.EOF when the stream is complete. +func (c *Codec) DeserializeEventMessage(schema *smithy.Schema, types *smithy.TypeRegistry, r io.Reader) (smithy.Deserializable, error) { + for { + c.payloadBuf = c.payloadBuf[0:0] + msg, err := c.dec().Decode(r, c.payloadBuf) + if err != nil { + if isEOF(err) { + return nil, io.EOF + } + return nil, fmt.Errorf("decode event: %w", err) + } + + msgType := msg.Headers.Get(MessageTypeHeader) + if msgType == nil { + return nil, fmt.Errorf("missing %s header", MessageTypeHeader) + } + + switch msgType.String() { + case EventMessageType: + event, err := c.deserializeEvent(schema, types, &msg) + if err != nil { + return nil, err + } + return event, nil + case ExceptionMessageType: + return nil, c.deserializeException(schema, types, &msg) + case ErrorMessageType: + return nil, deserializeError(&msg) + default: + mc := msg.Clone() + return nil, &UnknownMessageError{ + Type: msgType.String(), + Message: &mc, + } + } + } +} + +func (c *Codec) deserializeEvent(schema *smithy.Schema, types *smithy.TypeRegistry, msg *Message) (smithy.Deserializable, error) { + eventType := msg.Headers.Get(EventTypeHeader) + if eventType == nil { + return nil, fmt.Errorf("missing %s header", EventTypeHeader) + } + + member := schema.Member(eventType.String()) + if member == nil { + return c.unknownEvent(eventType.String(), msg) + } + + entry, ok := types.LookupEntry(member.TargetID().String()) + if !ok { + return c.unknownEvent(eventType.String(), msg) + } + + instance, ok := entry.New().(smithy.Deserializable) + if !ok { + return nil, fmt.Errorf("event type %s is not deserializable", eventType.String()) + } + + inner := c.Codec.Deserializer(msg.Payload) + ed := NewShapeDeserializer(msg, inner) + if err := instance.Deserialize(ed); err != nil { + return nil, fmt.Errorf("deserialize event %s: %w", eventType.String(), err) + } + + return instance, nil +} + +func (c *Codec) unknownEvent(tag string, msg *Message) (*UnknownUnionMember, error) { + var buf bytes.Buffer + c.enc().Encode(&buf, *msg) + return &UnknownUnionMember{Tag: tag, Value: buf.Bytes()}, nil +} + +func (c *Codec) deserializeException(schema *smithy.Schema, types *smithy.TypeRegistry, msg *Message) error { + exType := msg.Headers.Get(ExceptionTypeHeader) + if exType == nil { + return fmt.Errorf("missing %s header", ExceptionTypeHeader) + } + + var id string + if member := schema.Member(exType.String()); member != nil { + id = member.TargetID().String() + } else { + id = exType.String() + } + + perr, ok := types.DeserializableError(id) + if !ok { + return &smithy.GenericAPIError{ + Code: exType.String(), + Message: "unknown exception", + } + } + + inner := c.Codec.Deserializer(msg.Payload) + ed := NewShapeDeserializer(msg, inner) + if err := perr.Deserialize(ed); err != nil { + return fmt.Errorf("deserialize exception %s: %w", exType.String(), err) + } + + return perr +} + +// SerializeInitialRequest serializes the operation input as the first event +// stream message with :event-type "initial-request". +func (c *Codec) SerializeInitialRequest(schema *smithy.Schema, v smithy.Serializable, w io.Writer) error { + ss := c.Codec.Serializer() + v.Serialize(ss) + + var msg Message + msg.Headers.Set(MessageTypeHeader, StringValue(EventMessageType)) + msg.Headers.Set(EventTypeHeader, StringValue("initial-request")) + if payload := ss.Bytes(); len(payload) > 0 { + msg.Payload = payload + msg.Headers.Set(ContentTypeHeader, StringValue(c.ContentType)) + } + + return c.enc().Encode(w, msg) +} + +// DeserializeInitialResponse reads the first event stream message and +// deserializes it as the operation output. +func (c *Codec) DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, out smithy.Deserializable) error { + c.payloadBuf = c.payloadBuf[0:0] + msg, err := c.dec().Decode(r, c.payloadBuf) + if err != nil { + return fmt.Errorf("decode initial response: %w", err) + } + + eventType := msg.Headers.Get(EventTypeHeader) + if eventType == nil || eventType.String() != "initial-response" { + return fmt.Errorf("expected initial-response, got %v", eventType) + } + + if len(msg.Payload) > 0 { + sd := c.Codec.Deserializer(msg.Payload) + if err := out.Deserialize(sd); err != nil { + return fmt.Errorf("deserialize initial response: %w", err) + } + } + + return nil +} + +// UnknownUnionMember is returned when a union member is returned over the +// wire, but has an unknown tag. +type UnknownUnionMember struct { + Tag string + Value []byte +} + +// Deserialize is a no-op. The raw bytes are already captured in Value. +func (*UnknownUnionMember) Deserialize(smithy.ShapeDeserializer) error { + return nil +} + +// UnknownMessageError provides an error when a message is received from the +// stream, but the reader is unable to determine what kind of message it is. +type UnknownMessageError struct { + Type string + Message *Message +} + +func (e *UnknownMessageError) Error() string { + return "unknown event stream message type, " + e.Type +} + +func deserializeError(msg *Message) error { + code := "UnknownError" + message := code + if v := msg.Headers.Get(ErrorCodeHeader); v != nil { + code = v.String() + } + if v := msg.Headers.Get(ErrorMessageHeader); v != nil { + message = v.String() + } + return &smithy.GenericAPIError{ + Code: code, + Message: message, + } +} + +func isEOF(err error) bool { + return err == io.EOF || err == io.ErrUnexpectedEOF +} diff --git a/eventstream/const.go b/eventstream/const.go new file mode 100644 index 000000000..893156c5d --- /dev/null +++ b/eventstream/const.go @@ -0,0 +1,24 @@ +package eventstream + +// EventStream headers with specific meaning to async API functionality. +const ( + ChunkSignatureHeader = `:chunk-signature` // chunk signature for message + DateHeader = `:date` // Date header for signature + ContentTypeHeader = ":content-type" // message payload content-type + + // Message header and values + MessageTypeHeader = `:message-type` // Identifies type of message. + EventMessageType = `event` + ErrorMessageType = `error` + ExceptionMessageType = `exception` + + // Message Events + EventTypeHeader = `:event-type` // Identifies message event type e.g. "Stats". + + // Message Error + ErrorCodeHeader = `:error-code` + ErrorMessageHeader = `:error-message` + + // Message Exception + ExceptionTypeHeader = `:exception-type` +) diff --git a/eventstream/debug.go b/eventstream/debug.go new file mode 100644 index 000000000..6049402b1 --- /dev/null +++ b/eventstream/debug.go @@ -0,0 +1,144 @@ +package eventstream + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "strconv" +) + +type decodedMessage struct { + rawMessage + Headers decodedHeaders `json:"headers"` +} +type jsonMessage struct { + Length json.Number `json:"total_length"` + HeadersLen json.Number `json:"headers_length"` + PreludeCRC json.Number `json:"prelude_crc"` + Headers decodedHeaders `json:"headers"` + Payload []byte `json:"payload"` + CRC json.Number `json:"message_crc"` +} + +func (d *decodedMessage) UnmarshalJSON(b []byte) (err error) { + var jsonMsg jsonMessage + if err = json.Unmarshal(b, &jsonMsg); err != nil { + return err + } + + d.Length, err = numAsUint32(jsonMsg.Length) + if err != nil { + return err + } + d.HeadersLen, err = numAsUint32(jsonMsg.HeadersLen) + if err != nil { + return err + } + d.PreludeCRC, err = numAsUint32(jsonMsg.PreludeCRC) + if err != nil { + return err + } + d.Headers = jsonMsg.Headers + d.Payload = jsonMsg.Payload + d.CRC, err = numAsUint32(jsonMsg.CRC) + if err != nil { + return err + } + + return nil +} + +func (d *decodedMessage) MarshalJSON() ([]byte, error) { + jsonMsg := jsonMessage{ + Length: json.Number(strconv.Itoa(int(d.Length))), + HeadersLen: json.Number(strconv.Itoa(int(d.HeadersLen))), + PreludeCRC: json.Number(strconv.Itoa(int(d.PreludeCRC))), + Headers: d.Headers, + Payload: d.Payload, + CRC: json.Number(strconv.Itoa(int(d.CRC))), + } + + return json.Marshal(jsonMsg) +} + +func numAsUint32(n json.Number) (uint32, error) { + v, err := n.Int64() + if err != nil { + return 0, fmt.Errorf("failed to get int64 json number, %v", err) + } + + return uint32(v), nil +} + +func (d decodedMessage) Message() Message { + return Message{ + Headers: Headers(d.Headers), + Payload: d.Payload, + } +} + +type decodedHeaders Headers + +func (hs *decodedHeaders) UnmarshalJSON(b []byte) error { + var jsonHeaders []struct { + Name string `json:"name"` + Type valueType `json:"type"` + Value any `json:"value"` + } + + decoder := json.NewDecoder(bytes.NewReader(b)) + decoder.UseNumber() + if err := decoder.Decode(&jsonHeaders); err != nil { + return err + } + + var headers Headers + for _, h := range jsonHeaders { + value, err := valueFromType(h.Type, h.Value) + if err != nil { + return err + } + headers.Set(h.Name, value) + } + *hs = decodedHeaders(headers) + + return nil +} + +func valueFromType(typ valueType, val any) (Value, error) { + switch typ { + case trueValueType: + return BoolValue(true), nil + case falseValueType: + return BoolValue(false), nil + case int8ValueType: + v, err := val.(json.Number).Int64() + return Int8Value(int8(v)), err + case int16ValueType: + v, err := val.(json.Number).Int64() + return Int16Value(int16(v)), err + case int32ValueType: + v, err := val.(json.Number).Int64() + return Int32Value(int32(v)), err + case int64ValueType: + v, err := val.(json.Number).Int64() + return Int64Value(v), err + case bytesValueType: + v, err := base64.StdEncoding.DecodeString(val.(string)) + return BytesValue(v), err + case stringValueType: + v, err := base64.StdEncoding.DecodeString(val.(string)) + return StringValue(string(v)), err + case timestampValueType: + v, err := val.(json.Number).Int64() + return TimestampValue(timeFromEpochMilli(v)), err + case uuidValueType: + v, err := base64.StdEncoding.DecodeString(val.(string)) + var tv UUIDValue + copy(tv[:], v) + return tv, err + default: + panic(fmt.Sprintf("unknown type, %s, %T", typ.String(), val)) + } +} diff --git a/eventstream/decode.go b/eventstream/decode.go new file mode 100644 index 000000000..d9ab7652f --- /dev/null +++ b/eventstream/decode.go @@ -0,0 +1,218 @@ +package eventstream + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "encoding/json" + "fmt" + "github.com/aws/smithy-go/logging" + "hash" + "hash/crc32" + "io" +) + +// DecoderOptions is the Decoder configuration options. +type DecoderOptions struct { + Logger logging.Logger + LogMessages bool +} + +// Decoder provides decoding of an Event Stream messages. +type Decoder struct { + options DecoderOptions +} + +// NewDecoder initializes and returns a Decoder for decoding event +// stream messages from the reader provided. +func NewDecoder(optFns ...func(*DecoderOptions)) *Decoder { + options := DecoderOptions{} + + for _, fn := range optFns { + fn(&options) + } + + return &Decoder{ + options: options, + } +} + +// Decode attempts to decode a single message from the event stream reader. +// Will return the event stream message, or error if decodeMessage fails to read +// the message from the stream. +// +// payloadBuf is a byte slice that will be used in the returned Message.Payload. Callers +// must ensure that the Message.Payload from a previous decode has been consumed before passing in the same underlying +// payloadBuf byte slice. +func (d *Decoder) Decode(reader io.Reader, payloadBuf []byte) (m Message, err error) { + if d.options.Logger != nil && d.options.LogMessages { + debugMsgBuf := bytes.NewBuffer(nil) + reader = io.TeeReader(reader, debugMsgBuf) + defer func() { + logMessageDecode(d.options.Logger, debugMsgBuf, m, err) + }() + } + + m, err = decodeMessage(reader, payloadBuf) + + return m, err +} + +// decodeMessage attempts to decode a single message from the event stream reader. +// Will return the event stream message, or error if decodeMessage fails to read +// the message from the reader. +func decodeMessage(reader io.Reader, payloadBuf []byte) (m Message, err error) { + crc := crc32.New(crc32IEEETable) + hashReader := io.TeeReader(reader, crc) + + prelude, err := decodePrelude(hashReader, crc) + if err != nil { + return Message{}, err + } + + if prelude.HeadersLen > 0 { + lr := io.LimitReader(hashReader, int64(prelude.HeadersLen)) + m.Headers, err = decodeHeaders(lr) + if err != nil { + return Message{}, err + } + } + + if payloadLen := prelude.PayloadLen(); payloadLen > 0 { + buf, err := decodePayload(payloadBuf, io.LimitReader(hashReader, int64(payloadLen))) + if err != nil { + return Message{}, err + } + m.Payload = buf + } + + msgCRC := crc.Sum32() + if err := validateCRC(reader, msgCRC); err != nil { + return Message{}, err + } + + return m, nil +} + +func logMessageDecode(logger logging.Logger, msgBuf *bytes.Buffer, msg Message, decodeErr error) { + w := bytes.NewBuffer(nil) + defer func() { logger.Logf(logging.Debug, w.String()) }() + + fmt.Fprintf(w, "Raw message:\n%s\n", + hex.Dump(msgBuf.Bytes())) + + if decodeErr != nil { + fmt.Fprintf(w, "decodeMessage error: %v\n", decodeErr) + return + } + + rawMsg, err := msg.rawMessage() + if err != nil { + fmt.Fprintf(w, "failed to create raw message, %v\n", err) + return + } + + decodedMsg := decodedMessage{ + rawMessage: rawMsg, + Headers: decodedHeaders(msg.Headers), + } + + fmt.Fprintf(w, "Decoded message:\n") + encoder := json.NewEncoder(w) + if err := encoder.Encode(decodedMsg); err != nil { + fmt.Fprintf(w, "failed to generate decoded message, %v\n", err) + } +} + +func decodePrelude(r io.Reader, crc hash.Hash32) (messagePrelude, error) { + var p messagePrelude + + var err error + p.Length, err = decodeUint32(r) + if err != nil { + return messagePrelude{}, err + } + + p.HeadersLen, err = decodeUint32(r) + if err != nil { + return messagePrelude{}, err + } + + if err := p.ValidateLens(); err != nil { + return messagePrelude{}, err + } + + preludeCRC := crc.Sum32() + if err := validateCRC(r, preludeCRC); err != nil { + return messagePrelude{}, err + } + + p.PreludeCRC = preludeCRC + + return p, nil +} + +func decodePayload(buf []byte, r io.Reader) ([]byte, error) { + w := bytes.NewBuffer(buf[0:0]) + + _, err := io.Copy(w, r) + return w.Bytes(), err +} + +func decodeUint8(r io.Reader) (uint8, error) { + type byteReader interface { + ReadByte() (byte, error) + } + + if br, ok := r.(byteReader); ok { + v, err := br.ReadByte() + return v, err + } + + var b [1]byte + _, err := io.ReadFull(r, b[:]) + return b[0], err +} + +func decodeUint16(r io.Reader) (uint16, error) { + var b [2]byte + bs := b[:] + _, err := io.ReadFull(r, bs) + if err != nil { + return 0, err + } + return binary.BigEndian.Uint16(bs), nil +} + +func decodeUint32(r io.Reader) (uint32, error) { + var b [4]byte + bs := b[:] + _, err := io.ReadFull(r, bs) + if err != nil { + return 0, err + } + return binary.BigEndian.Uint32(bs), nil +} + +func decodeUint64(r io.Reader) (uint64, error) { + var b [8]byte + bs := b[:] + _, err := io.ReadFull(r, bs) + if err != nil { + return 0, err + } + return binary.BigEndian.Uint64(bs), nil +} + +func validateCRC(r io.Reader, expect uint32) error { + msgCRC, err := decodeUint32(r) + if err != nil { + return err + } + + if msgCRC != expect { + return ChecksumError{} + } + + return nil +} diff --git a/eventstream/decode_test.go b/eventstream/decode_test.go new file mode 100644 index 000000000..e73044d46 --- /dev/null +++ b/eventstream/decode_test.go @@ -0,0 +1,188 @@ +package eventstream + +import ( + "bytes" + "encoding/hex" + "errors" + "io/ioutil" + "os" + "reflect" + "testing" +) + +func TestWriteEncodedFromDecoded(t *testing.T) { + cases, err := readPositiveTests("testdata") + if err != nil { + t.Fatalf("failed to load positive tests, %v", err) + } + + for _, c := range cases { + f, err := ioutil.TempFile(os.TempDir(), "encoded_positive_"+c.Name) + if err != nil { + t.Fatalf("failed to open %q, %v", c.Name, err) + } + + encoder := NewEncoder() + + msg := c.Decoded.Message() + if err := encoder.Encode(f, msg); err != nil { + t.Errorf("failed to encode %q, %v", c.Name, err) + } + + if err = f.Close(); err != nil { + t.Errorf("expected %v, got %v", "no error", err) + } + if err = os.Remove(f.Name()); err != nil { + t.Errorf("expected %v, got %v", "no error", err) + } + } +} + +func TestDecoder_Decode(t *testing.T) { + cases, err := readPositiveTests("testdata") + if err != nil { + t.Fatalf("failed to load positive tests, %v", err) + } + + for _, c := range cases { + decoder := NewDecoder() + + msg, err := decoder.Decode(bytes.NewBuffer(c.Encoded), nil) + if err != nil { + t.Fatalf("%s, expect no decode error, got %v", c.Name, err) + } + + raw, err := msg.rawMessage() // rawMessage will fail if payload read CRC fails + if err != nil { + t.Fatalf("%s, failed to get raw decoded message %v", c.Name, err) + } + + if e, a := c.Decoded.Length, raw.Length; e != a { + t.Errorf("%s, expect %v length, got %v", c.Name, e, a) + } + if e, a := c.Decoded.HeadersLen, raw.HeadersLen; e != a { + t.Errorf("%s, expect %v HeadersLen, got %v", c.Name, e, a) + } + if e, a := c.Decoded.PreludeCRC, raw.PreludeCRC; e != a { + t.Errorf("%s, expect %v PreludeCRC, got %v", c.Name, e, a) + } + if e, a := Headers(c.Decoded.Headers), msg.Headers; !reflect.DeepEqual(e, a) { + t.Errorf("%s, expect %v headers, got %v", c.Name, e, a) + } + if e, a := c.Decoded.Payload, raw.Payload; !bytes.Equal(e, a) { + t.Errorf("%s, expect %v payload, got %v", c.Name, e, a) + } + if e, a := c.Decoded.CRC, raw.CRC; e != a { + t.Errorf("%s, expect %v CRC, got %v", c.Name, e, a) + } + } +} + +func TestDecoder_Decode_Negative(t *testing.T) { + cases, err := readNegativeTests("testdata") + if err != nil { + t.Fatalf("failed to load negative tests, %v", err) + } + + for _, c := range cases { + decoder := NewDecoder() + + msg, err := decoder.Decode(bytes.NewBuffer(c.Encoded), nil) + if err == nil { + rawMsg, rawMsgErr := msg.rawMessage() + t.Fatalf("%s, expect error, got none, %s,\n%s\n%#v, %v\n", c.Name, + c.Err, hex.Dump(c.Encoded), rawMsg, rawMsgErr) + } + } +} + +var testEncodedMsg = []byte{0, 0, 0, 61, 0, 0, 0, 32, 7, 253, 131, 150, 12, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 7, 0, 16, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 106, 115, 111, 110, 123, 39, 102, 111, 111, 39, 58, 39, 98, 97, 114, 39, 125, 141, 156, 8, 177} + +func TestDecoder_DecodeMultipleMessages(t *testing.T) { + const ( + expectMsgCount = 10 + expectPayloadLen = 13 + ) + + r := bytes.NewBuffer(nil) + for range expectMsgCount { + r.Write(testEncodedMsg) + } + + decoder := NewDecoder() + + var err error + var msg Message + var count int + for { + msg, err = decoder.Decode(r, nil) + if err != nil { + break + } + count++ + + if e, a := expectPayloadLen, len(msg.Payload); e != a { + t.Errorf("expect %v payload len, got %v", e, a) + } + + if e, a := []byte(`{'foo':'bar'}`), msg.Payload; !bytes.Equal(e, a) { + t.Errorf("expect %v payload, got %v", e, a) + } + } + + type causer interface { + Cause() error + } + if err != nil && count != expectMsgCount { + t.Fatalf("expect, no error, got %v", err) + } + + if e, a := expectMsgCount, count; e != a { + t.Errorf("expect %v messages read, got %v", e, a) + } +} + +func TestDecodeLimits(t *testing.T) { + l := 25 * 1024 * 1024 // Previously we failed if message was set to >16 MB + payload := bytes.Repeat([]byte{0x01}, l) // if set to just 0, message will be read as having 0 size. + buffer := bytes.NewBuffer(payload) + _, err := NewDecoder().Decode(buffer, nil) + if err == nil { + t.Fatalf("expect error since message is not properly encoded, got none") + } + if errors.As(err, &LengthError{}) { + t.Fatalf("expect error not being a length error, got %v", err) + } +} + +func BenchmarkDecode(b *testing.B) { + r := bytes.NewReader(testEncodedMsg) + decoder := NewDecoder() + payloadBuf := make([]byte, 0, 5*1024) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + msg, err := decoder.Decode(r, payloadBuf) + if err != nil { + b.Fatal(err) + } + + // Release the payload buffer + payloadBuf = msg.Payload[0:0] + r.Seek(0, 0) + } +} + +func BenchmarkDecode_NoPayloadBuf(b *testing.B) { + r := bytes.NewReader(testEncodedMsg) + decoder := NewDecoder() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + _, err := decoder.Decode(r, nil) + if err != nil { + b.Fatal(err) + } + r.Seek(0, 0) + } +} diff --git a/eventstream/deserializer.go b/eventstream/deserializer.go new file mode 100644 index 000000000..81f983c74 --- /dev/null +++ b/eventstream/deserializer.go @@ -0,0 +1,364 @@ +package eventstream + +import ( + "fmt" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/traits" +) + +// ShapeDeserializer wraps a [smithy.ShapeDeserializer] to handle event stream +// message binding traits. +type ShapeDeserializer struct { + Message *Message + + inner smithy.ShapeDeserializer + + depth int + schema *smithy.Schema + + bindings []*smithy.Schema + bindIdx int + inBindings bool + + inBody bool + hasPayload bool +} + +var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil) + +// NewShapeDeserializer returns a deserializer for a Message. +func NewShapeDeserializer(msg *Message, inner smithy.ShapeDeserializer) *ShapeDeserializer { + return &ShapeDeserializer{ + Message: msg, + inner: inner, + } +} + +func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error { + d.depth++ + if d.depth > 1 { + return d.inner.ReadStruct(s) + } + d.schema = s + for _, m := range s.Members() { + if _, ok := smithy.SchemaTrait[*traits.EventPayload](m); ok { + d.hasPayload = true + } + if isEventBound(m) { + d.bindings = append(d.bindings, m) + } + } + return nil +} + +func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { + if d.depth > 1 { + ms, err := d.inner.ReadStructMember() + if ms == nil { + d.depth-- + } + return ms, err + } + + // like httpbinding, throw back the bound stuff first before we drop into + // the body + if d.bindIdx < len(d.bindings) { + m := d.bindings[d.bindIdx] + d.bindIdx++ + d.inBindings = true + return m, nil + } + d.inBindings = false + + if d.hasPayload { + d.depth-- + return nil, nil + } + + if !d.inBody { + d.inBody = true + if err := d.inner.ReadStruct(d.schema); err != nil { + return nil, err + } + } + + ms, err := d.inner.ReadStructMember() + if ms == nil { + d.depth-- + } + + return ms, err +} + +func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { + if d.inBindings { + if isEventHeader(s) { + hv := d.Message.Headers.Get(s.MemberName()) + if hv == nil { + return nil + } + sv, ok := hv.(StringValue) + if !ok { + return fmt.Errorf("event header %q: expected string, got %T", s.MemberName(), hv) + } + *v = string(sv) + return nil + } + if isEventPayload(s) { + *v = string(d.Message.Payload) + return nil + } + } + return d.inner.ReadString(s, v) +} + +func (d *ShapeDeserializer) ReadStringPtr(s *smithy.Schema, v **string) error { + if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { + return nil + } + var val string + if err := d.ReadString(s, &val); err != nil { + return err + } + *v = &val + return nil +} + +func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { + if d.inBindings && isEventHeader(s) { + hv := d.Message.Headers.Get(s.MemberName()) + if hv == nil { + return nil + } + bv, ok := hv.(BoolValue) + if !ok { + return fmt.Errorf("event header %q: expected bool, got %T", s.MemberName(), hv) + } + *v = bool(bv) + return nil + } + return d.inner.ReadBool(s, v) +} + +func (d *ShapeDeserializer) ReadBoolPtr(s *smithy.Schema, v **bool) error { + if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { + return nil + } + var val bool + if err := d.ReadBool(s, &val); err != nil { + return err + } + *v = &val + return nil +} + +func (d *ShapeDeserializer) readHeaderInt64(name string) (int64, bool, error) { + hv := d.Message.Headers.Get(name) + if hv == nil { + return 0, false, nil + } + switch v := hv.(type) { + case Int8Value: + return int64(v), true, nil + case Int16Value: + return int64(v), true, nil + case Int32Value: + return int64(v), true, nil + case Int64Value: + return int64(v), true, nil + default: + return 0, false, fmt.Errorf("event header %q: expected integer, got %T", name, hv) + } +} + +type intn interface { + int8 | int16 | int32 | int64 +} + +func readEventHeaderInt[T intn](d *ShapeDeserializer, s *smithy.Schema, v *T) error { + n, ok, err := d.readHeaderInt64(s.MemberName()) + if err != nil || !ok { + return err + } + *v = T(n) + return nil +} + +func (d *ShapeDeserializer) ReadInt8(s *smithy.Schema, v *int8) error { + if d.inBindings && isEventHeader(s) { + return readEventHeaderInt(d, s, v) + } + return d.inner.ReadInt8(s, v) +} + +func (d *ShapeDeserializer) ReadInt8Ptr(s *smithy.Schema, v **int8) error { + if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { + return nil + } + var val int8 + if err := d.ReadInt8(s, &val); err != nil { + return err + } + *v = &val + return nil +} + +func (d *ShapeDeserializer) ReadInt16(s *smithy.Schema, v *int16) error { + if d.inBindings && isEventHeader(s) { + return readEventHeaderInt(d, s, v) + } + return d.inner.ReadInt16(s, v) +} + +func (d *ShapeDeserializer) ReadInt16Ptr(s *smithy.Schema, v **int16) error { + if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { + return nil + } + var val int16 + if err := d.ReadInt16(s, &val); err != nil { + return err + } + *v = &val + return nil +} + +func (d *ShapeDeserializer) ReadInt32(s *smithy.Schema, v *int32) error { + if d.inBindings && isEventHeader(s) { + return readEventHeaderInt(d, s, v) + } + return d.inner.ReadInt32(s, v) +} + +func (d *ShapeDeserializer) ReadInt32Ptr(s *smithy.Schema, v **int32) error { + if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { + return nil + } + var val int32 + if err := d.ReadInt32(s, &val); err != nil { + return err + } + *v = &val + return nil +} + +func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { + if d.inBindings && isEventHeader(s) { + return readEventHeaderInt(d, s, v) + } + return d.inner.ReadInt64(s, v) +} + +func (d *ShapeDeserializer) ReadInt64Ptr(s *smithy.Schema, v **int64) error { + if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { + return nil + } + var val int64 + if err := d.ReadInt64(s, &val); err != nil { + return err + } + *v = &val + return nil +} + +func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error { + return d.inner.ReadFloat32(s, v) +} + +func (d *ShapeDeserializer) ReadFloat32Ptr(s *smithy.Schema, v **float32) error { + return d.inner.ReadFloat32Ptr(s, v) +} + +func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { + return d.inner.ReadFloat64(s, v) +} + +func (d *ShapeDeserializer) ReadFloat64Ptr(s *smithy.Schema, v **float64) error { + return d.inner.ReadFloat64Ptr(s, v) +} + +func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { + if d.inBindings { + if isEventHeader(s) { + hv := d.Message.Headers.Get(s.MemberName()) + if hv == nil { + return nil + } + bv, ok := hv.(BytesValue) + if !ok { + return fmt.Errorf("event header %q: expected bytes, got %T", s.MemberName(), hv) + } + *v = []byte(bv) + return nil + } + if isEventPayload(s) { + *v = d.Message.Payload + return nil + } + } + return d.inner.ReadBlob(s, v) +} + +func (d *ShapeDeserializer) ReadTime(s *smithy.Schema, v *time.Time) error { + if d.inBindings && isEventHeader(s) { + hv := d.Message.Headers.Get(s.MemberName()) + if hv == nil { + return nil + } + tv, ok := hv.(TimestampValue) + if !ok { + return fmt.Errorf("event header %q: expected timestamp, got %T", s.MemberName(), hv) + } + *v = time.Time(tv) + return nil + } + return d.inner.ReadTime(s, v) +} + +func (d *ShapeDeserializer) ReadTimePtr(s *smithy.Schema, v **time.Time) error { + if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { + return nil + } + var val time.Time + if err := d.ReadTime(s, &val); err != nil { + return err + } + *v = &val + return nil +} + +func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { + return d.inner.ReadList(s) +} + +func (d *ShapeDeserializer) ReadListItem(s *smithy.Schema) (bool, error) { + return d.inner.ReadListItem(s) +} + +func (d *ShapeDeserializer) ReadMap(s *smithy.Schema) error { + return d.inner.ReadMap(s) +} + +func (d *ShapeDeserializer) ReadMapKey(s *smithy.Schema) (string, bool, error) { + return d.inner.ReadMapKey(s) +} + +func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) { + return d.inner.ReadUnion(s) +} + +func (d *ShapeDeserializer) ReadNil(s *smithy.Schema) (bool, error) { + return d.inner.ReadNil(s) +} + +func (d *ShapeDeserializer) ReadDocument(s *smithy.Schema, v *document.Value) error { + return d.inner.ReadDocument(s, v) +} + +func isEventBound(schema *smithy.Schema) bool { + _, h := smithy.SchemaTrait[*traits.EventHeader](schema) + _, p := smithy.SchemaTrait[*traits.EventPayload](schema) + return h || p +} diff --git a/eventstream/encode.go b/eventstream/encode.go new file mode 100644 index 000000000..61cf7238d --- /dev/null +++ b/eventstream/encode.go @@ -0,0 +1,167 @@ +package eventstream + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "encoding/json" + "fmt" + "github.com/aws/smithy-go/logging" + "hash" + "hash/crc32" + "io" +) + +// EncoderOptions is the configuration options for Encoder. +type EncoderOptions struct { + Logger logging.Logger + LogMessages bool +} + +// Encoder provides EventStream message encoding. +type Encoder struct { + options EncoderOptions + + headersBuf *bytes.Buffer + messageBuf *bytes.Buffer +} + +// NewEncoder initializes and returns an Encoder to encode Event Stream +// messages. +func NewEncoder(optFns ...func(*EncoderOptions)) *Encoder { + o := EncoderOptions{} + + for _, fn := range optFns { + fn(&o) + } + + return &Encoder{ + options: o, + headersBuf: bytes.NewBuffer(nil), + messageBuf: bytes.NewBuffer(nil), + } +} + +// Encode encodes a single EventStream message to the io.Writer the Encoder +// was created with. An error is returned if writing the message fails. +func (e *Encoder) Encode(w io.Writer, msg Message) (err error) { + e.headersBuf.Reset() + e.messageBuf.Reset() + + var writer io.Writer = e.messageBuf + if e.options.Logger != nil && e.options.LogMessages { + encodeMsgBuf := bytes.NewBuffer(nil) + writer = io.MultiWriter(writer, encodeMsgBuf) + defer func() { + logMessageEncode(e.options.Logger, encodeMsgBuf, msg, err) + }() + } + + if err = EncodeHeaders(e.headersBuf, msg.Headers); err != nil { + return err + } + + crc := crc32.New(crc32IEEETable) + hashWriter := io.MultiWriter(writer, crc) + + headersLen := uint32(e.headersBuf.Len()) + payloadLen := uint32(len(msg.Payload)) + + if err = encodePrelude(hashWriter, crc, headersLen, payloadLen); err != nil { + return err + } + + if headersLen > 0 { + if _, err = io.Copy(hashWriter, e.headersBuf); err != nil { + return err + } + } + + if payloadLen > 0 { + if _, err = hashWriter.Write(msg.Payload); err != nil { + return err + } + } + + msgCRC := crc.Sum32() + if err := binary.Write(writer, binary.BigEndian, msgCRC); err != nil { + return err + } + + _, err = io.Copy(w, e.messageBuf) + + return err +} + +func logMessageEncode(logger logging.Logger, msgBuf *bytes.Buffer, msg Message, encodeErr error) { + w := bytes.NewBuffer(nil) + defer func() { logger.Logf(logging.Debug, w.String()) }() + + fmt.Fprintf(w, "Message to encode:\n") + encoder := json.NewEncoder(w) + if err := encoder.Encode(msg); err != nil { + fmt.Fprintf(w, "Failed to get encoded message, %v\n", err) + } + + if encodeErr != nil { + fmt.Fprintf(w, "Encode error: %v\n", encodeErr) + return + } + + fmt.Fprintf(w, "Raw message:\n%s\n", hex.Dump(msgBuf.Bytes())) +} + +func encodePrelude(w io.Writer, crc hash.Hash32, headersLen, payloadLen uint32) error { + p := messagePrelude{ + Length: minMsgLen + headersLen + payloadLen, + HeadersLen: headersLen, + } + if err := p.ValidateLens(); err != nil { + return err + } + + err := binaryWriteFields(w, binary.BigEndian, + p.Length, + p.HeadersLen, + ) + if err != nil { + return err + } + + p.PreludeCRC = crc.Sum32() + err = binary.Write(w, binary.BigEndian, p.PreludeCRC) + if err != nil { + return err + } + + return nil +} + +// EncodeHeaders writes the header values to the writer encoded in the event +// stream format. Returns an error if a header fails to encode. +func EncodeHeaders(w io.Writer, headers Headers) error { + for _, h := range headers { + hn := headerName{ + Len: uint8(len(h.Name)), + } + copy(hn.Name[:hn.Len], h.Name) + if err := hn.encode(w); err != nil { + return err + } + + if err := h.Value.encode(w); err != nil { + return err + } + } + + return nil +} + +func binaryWriteFields(w io.Writer, order binary.ByteOrder, vs ...any) error { + for _, v := range vs { + if err := binary.Write(w, order, v); err != nil { + return err + } + } + return nil +} diff --git a/eventstream/encode_test.go b/eventstream/encode_test.go new file mode 100644 index 000000000..6d38e7ade --- /dev/null +++ b/eventstream/encode_test.go @@ -0,0 +1,75 @@ +package eventstream + +import ( + "bytes" + "encoding/hex" + "io" + "reflect" + "testing" +) + +func TestEncoder_Encode(t *testing.T) { + cases, err := readPositiveTests("testdata") + if err != nil { + t.Fatalf("failed to load positive tests, %v", err) + } + + for _, c := range cases { + var w bytes.Buffer + encoder := NewEncoder() + + err = encoder.Encode(&w, c.Decoded.Message()) + if err != nil { + t.Fatalf("%s, failed to encode message, %v", c.Name, err) + } + + if e, a := c.Encoded, w.Bytes(); !reflect.DeepEqual(e, a) { + t.Errorf("%s, expect:\n%v\nactual:\n%v\n", c.Name, + hex.Dump(e), hex.Dump(a)) + } + } +} + +func BenchmarkEncode(b *testing.B) { + var w bytes.Buffer + encoder := NewEncoder() + msg := Message{ + Headers: Headers{ + {Name: "event-id", Value: Int16Value(123)}, + }, + Payload: []byte(`{"abc":123}`), + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + err := encoder.Encode(&w, msg) + if err != nil { + b.Fatal(err) + } + } +} + +func TestEncoder_Limits(t *testing.T) { + l := 25 * 1024 * 1024 // Previously we failed if message was set to >16 MB + payload := make([]byte, l) + encoder := NewEncoder() + err := encoder.Encode(io.Discard, Message{Payload: payload}) + if err != nil { + t.Fatalf("Expected encoder being able to encode %d size, failed with %v", l, err) + } + + h := Header{ + Name: "event-id", Value: Int16Value(123), + } + + headers := make(Headers, 0, 10_000) // Previously we failed if headers size was above a certain size + for range 10_000 { + headers = append(headers, h) + } + + err = encoder.Encode(io.Discard, Message{Headers: headers}) + if err != nil { + t.Fatalf("Expected encoder being able to encode %d size, failed with %v", l, err) + } +} diff --git a/eventstream/error.go b/eventstream/error.go new file mode 100644 index 000000000..7616214dd --- /dev/null +++ b/eventstream/error.go @@ -0,0 +1,23 @@ +package eventstream + +import "fmt" + +// LengthError provides the error for items being larger than a maximum length. +type LengthError struct { + Part string + Want int + Have int + Value any +} + +func (e LengthError) Error() string { + return fmt.Sprintf("%s length invalid, %d/%d, %v", + e.Part, e.Want, e.Have, e.Value) +} + +// ChecksumError provides the error for message checksum invalidation errors. +type ChecksumError struct{} + +func (e ChecksumError) Error() string { + return "message checksum mismatch" +} diff --git a/eventstream/header.go b/eventstream/header.go new file mode 100644 index 000000000..f580bda4c --- /dev/null +++ b/eventstream/header.go @@ -0,0 +1,175 @@ +package eventstream + +import ( + "encoding/binary" + "fmt" + "io" +) + +// Headers are a collection of EventStream header values. +type Headers []Header + +// Header is a single EventStream Key Value header pair. +type Header struct { + Name string + Value Value +} + +// Set associates the name with a value. If the header name already exists in +// the Headers the value will be replaced with the new one. +func (hs *Headers) Set(name string, value Value) { + var i int + for ; i < len(*hs); i++ { + if (*hs)[i].Name == name { + (*hs)[i].Value = value + return + } + } + + *hs = append(*hs, Header{ + Name: name, Value: value, + }) +} + +// Get returns the Value associated with the header. Nil is returned if the +// value does not exist. +func (hs Headers) Get(name string) Value { + for i := range hs { + if h := hs[i]; h.Name == name { + return h.Value + } + } + return nil +} + +// Del deletes the value in the Headers if it exists. +func (hs *Headers) Del(name string) { + for i := 0; i < len(*hs); i++ { + if (*hs)[i].Name == name { + copy((*hs)[i:], (*hs)[i+1:]) + (*hs) = (*hs)[:len(*hs)-1] + } + } +} + +// Clone returns a deep copy of the headers +func (hs Headers) Clone() Headers { + o := make(Headers, 0, len(hs)) + for _, h := range hs { + o.Set(h.Name, h.Value) + } + return o +} + +func decodeHeaders(r io.Reader) (Headers, error) { + hs := Headers{} + + for { + name, err := decodeHeaderName(r) + if err != nil { + if err == io.EOF { + // EOF while getting header name means no more headers + break + } + return nil, err + } + + value, err := decodeHeaderValue(r) + if err != nil { + return nil, err + } + + hs.Set(name, value) + } + + return hs, nil +} + +func decodeHeaderName(r io.Reader) (string, error) { + var n headerName + + var err error + n.Len, err = decodeUint8(r) + if err != nil { + return "", err + } + + name := n.Name[:n.Len] + if _, err := io.ReadFull(r, name); err != nil { + return "", err + } + + return string(name), nil +} + +func decodeHeaderValue(r io.Reader) (Value, error) { + var raw rawValue + + typ, err := decodeUint8(r) + if err != nil { + return nil, err + } + raw.Type = valueType(typ) + + var v Value + + switch raw.Type { + case trueValueType: + v = BoolValue(true) + case falseValueType: + v = BoolValue(false) + case int8ValueType: + var tv Int8Value + err = tv.decode(r) + v = tv + case int16ValueType: + var tv Int16Value + err = tv.decode(r) + v = tv + case int32ValueType: + var tv Int32Value + err = tv.decode(r) + v = tv + case int64ValueType: + var tv Int64Value + err = tv.decode(r) + v = tv + case bytesValueType: + var tv BytesValue + err = tv.decode(r) + v = tv + case stringValueType: + var tv StringValue + err = tv.decode(r) + v = tv + case timestampValueType: + var tv TimestampValue + err = tv.decode(r) + v = tv + case uuidValueType: + var tv UUIDValue + err = tv.decode(r) + v = tv + default: + panic(fmt.Sprintf("unknown value type %d", raw.Type)) + } + + // Error could be EOF, let caller deal with it + return v, err +} + +const maxHeaderNameLen = 255 + +type headerName struct { + Len uint8 + Name [maxHeaderNameLen]byte +} + +func (v headerName) encode(w io.Writer) error { + if err := binary.Write(w, binary.BigEndian, v.Len); err != nil { + return err + } + + _, err := w.Write(v.Name[:v.Len]) + return err +} diff --git a/eventstream/header_test.go b/eventstream/header_test.go new file mode 100644 index 000000000..d28096e43 --- /dev/null +++ b/eventstream/header_test.go @@ -0,0 +1,66 @@ +package eventstream + +import ( + "reflect" + "testing" + "time" +) + +func TestHeaders_Set(t *testing.T) { + expect := Headers{ + {Name: "ABC", Value: StringValue("123")}, + {Name: "EFG", Value: TimestampValue(time.Time{})}, + } + + var actual Headers + actual.Set("ABC", Int32Value(123)) + actual.Set("ABC", StringValue("123")) // replace case + actual.Set("EFG", TimestampValue(time.Time{})) + + if e, a := expect, actual; !reflect.DeepEqual(e, a) { + t.Errorf("expect %v headers, got %v", e, a) + } +} + +func TestHeaders_Get(t *testing.T) { + headers := Headers{ + {Name: "ABC", Value: StringValue("123")}, + {Name: "EFG", Value: TimestampValue(time.Time{})}, + } + + cases := []struct { + Name string + Value Value + }{ + {Name: "ABC", Value: StringValue("123")}, + {Name: "EFG", Value: TimestampValue(time.Time{})}, + {Name: "NotFound"}, + } + + for i, c := range cases { + actual := headers.Get(c.Name) + if e, a := c.Value, actual; !reflect.DeepEqual(e, a) { + t.Errorf("%d, expect %v value, got %v", i, e, a) + } + } +} + +func TestHeaders_Del(t *testing.T) { + headers := Headers{ + {Name: "ABC", Value: StringValue("123")}, + {Name: "EFG", Value: TimestampValue(time.Time{})}, + {Name: "HIJ", Value: StringValue("123")}, + {Name: "KML", Value: TimestampValue(time.Time{})}, + } + expectAfterDel := Headers{ + {Name: "EFG", Value: TimestampValue(time.Time{})}, + } + + headers.Del("HIJ") + headers.Del("ABC") + headers.Del("KML") + + if e, a := expectAfterDel, headers; !reflect.DeepEqual(e, a) { + t.Errorf("expect %v headers, got %v", e, a) + } +} diff --git a/eventstream/header_value.go b/eventstream/header_value.go new file mode 100644 index 000000000..61ed35366 --- /dev/null +++ b/eventstream/header_value.go @@ -0,0 +1,521 @@ +package eventstream + +import ( + "encoding/base64" + "encoding/binary" + "encoding/hex" + "fmt" + "io" + "strconv" + "time" +) + +const maxHeaderValueLen = 1<<15 - 1 // 2^15-1 or 32KB - 1 + +// valueType is the EventStream header value type. +type valueType uint8 + +// Header value types +const ( + trueValueType valueType = iota + falseValueType + int8ValueType // Byte + int16ValueType // Short + int32ValueType // Integer + int64ValueType // Long + bytesValueType + stringValueType + timestampValueType + uuidValueType +) + +func (t valueType) String() string { + switch t { + case trueValueType: + return "bool" + case falseValueType: + return "bool" + case int8ValueType: + return "int8" + case int16ValueType: + return "int16" + case int32ValueType: + return "int32" + case int64ValueType: + return "int64" + case bytesValueType: + return "byte_array" + case stringValueType: + return "string" + case timestampValueType: + return "timestamp" + case uuidValueType: + return "uuid" + default: + return fmt.Sprintf("unknown value type %d", uint8(t)) + } +} + +type rawValue struct { + Type valueType + Len uint16 // Only set for variable length slices + Value []byte // byte representation of value, BigEndian encoding. +} + +func (r rawValue) encodeScalar(w io.Writer, v any) error { + return binaryWriteFields(w, binary.BigEndian, + r.Type, + v, + ) +} + +func (r rawValue) encodeFixedSlice(w io.Writer, v []byte) error { + binary.Write(w, binary.BigEndian, r.Type) + + _, err := w.Write(v) + return err +} + +func (r rawValue) encodeBytes(w io.Writer, v []byte) error { + if len(v) > maxHeaderValueLen { + return LengthError{ + Part: "header value", + Want: maxHeaderValueLen, Have: len(v), + Value: v, + } + } + r.Len = uint16(len(v)) + + err := binaryWriteFields(w, binary.BigEndian, + r.Type, + r.Len, + ) + if err != nil { + return err + } + + _, err = w.Write(v) + return err +} + +func (r rawValue) encodeString(w io.Writer, v string) error { + if len(v) > maxHeaderValueLen { + return LengthError{ + Part: "header value", + Want: maxHeaderValueLen, Have: len(v), + Value: v, + } + } + r.Len = uint16(len(v)) + + type stringWriter interface { + WriteString(string) (int, error) + } + + err := binaryWriteFields(w, binary.BigEndian, + r.Type, + r.Len, + ) + if err != nil { + return err + } + + if sw, ok := w.(stringWriter); ok { + _, err = sw.WriteString(v) + } else { + _, err = w.Write([]byte(v)) + } + + return err +} + +func decodeFixedBytesValue(r io.Reader, buf []byte) error { + _, err := io.ReadFull(r, buf) + return err +} + +func decodeBytesValue(r io.Reader) ([]byte, error) { + var raw rawValue + var err error + raw.Len, err = decodeUint16(r) + if err != nil { + return nil, err + } + + buf := make([]byte, raw.Len) + _, err = io.ReadFull(r, buf) + if err != nil { + return nil, err + } + + return buf, nil +} + +func decodeStringValue(r io.Reader) (string, error) { + v, err := decodeBytesValue(r) + return string(v), err +} + +// Value represents the abstract header value. +type Value interface { + Get() any + String() string + valueType() valueType + encode(io.Writer) error +} + +// An BoolValue provides eventstream encoding, and representation +// of a Go bool value. +type BoolValue bool + +// Get returns the underlying type +func (v BoolValue) Get() any { + return bool(v) +} + +// valueType returns the EventStream header value type value. +func (v BoolValue) valueType() valueType { + if v { + return trueValueType + } + return falseValueType +} + +func (v BoolValue) String() string { + return strconv.FormatBool(bool(v)) +} + +// encode encodes the BoolValue into an eventstream binary value +// representation. +func (v BoolValue) encode(w io.Writer) error { + return binary.Write(w, binary.BigEndian, v.valueType()) +} + +// An Int8Value provides eventstream encoding, and representation of a Go +// int8 value. +type Int8Value int8 + +// Get returns the underlying value. +func (v Int8Value) Get() any { + return int8(v) +} + +// valueType returns the EventStream header value type value. +func (Int8Value) valueType() valueType { + return int8ValueType +} + +func (v Int8Value) String() string { + return fmt.Sprintf("0x%02x", int8(v)) +} + +// encode encodes the Int8Value into an eventstream binary value +// representation. +func (v Int8Value) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + return raw.encodeScalar(w, v) +} + +func (v *Int8Value) decode(r io.Reader) error { + n, err := decodeUint8(r) + if err != nil { + return err + } + + *v = Int8Value(n) + return nil +} + +// An Int16Value provides eventstream encoding, and representation of a Go +// int16 value. +type Int16Value int16 + +// Get returns the underlying value. +func (v Int16Value) Get() any { + return int16(v) +} + +// valueType returns the EventStream header value type value. +func (Int16Value) valueType() valueType { + return int16ValueType +} + +func (v Int16Value) String() string { + return fmt.Sprintf("0x%04x", int16(v)) +} + +// encode encodes the Int16Value into an eventstream binary value +// representation. +func (v Int16Value) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + return raw.encodeScalar(w, v) +} + +func (v *Int16Value) decode(r io.Reader) error { + n, err := decodeUint16(r) + if err != nil { + return err + } + + *v = Int16Value(n) + return nil +} + +// An Int32Value provides eventstream encoding, and representation of a Go +// int32 value. +type Int32Value int32 + +// Get returns the underlying value. +func (v Int32Value) Get() any { + return int32(v) +} + +// valueType returns the EventStream header value type value. +func (Int32Value) valueType() valueType { + return int32ValueType +} + +func (v Int32Value) String() string { + return fmt.Sprintf("0x%08x", int32(v)) +} + +// encode encodes the Int32Value into an eventstream binary value +// representation. +func (v Int32Value) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + return raw.encodeScalar(w, v) +} + +func (v *Int32Value) decode(r io.Reader) error { + n, err := decodeUint32(r) + if err != nil { + return err + } + + *v = Int32Value(n) + return nil +} + +// An Int64Value provides eventstream encoding, and representation of a Go +// int64 value. +type Int64Value int64 + +// Get returns the underlying value. +func (v Int64Value) Get() any { + return int64(v) +} + +// valueType returns the EventStream header value type value. +func (Int64Value) valueType() valueType { + return int64ValueType +} + +func (v Int64Value) String() string { + return fmt.Sprintf("0x%016x", int64(v)) +} + +// encode encodes the Int64Value into an eventstream binary value +// representation. +func (v Int64Value) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + return raw.encodeScalar(w, v) +} + +func (v *Int64Value) decode(r io.Reader) error { + n, err := decodeUint64(r) + if err != nil { + return err + } + + *v = Int64Value(n) + return nil +} + +// An BytesValue provides eventstream encoding, and representation of a Go +// byte slice. +type BytesValue []byte + +// Get returns the underlying value. +func (v BytesValue) Get() any { + return []byte(v) +} + +// valueType returns the EventStream header value type value. +func (BytesValue) valueType() valueType { + return bytesValueType +} + +func (v BytesValue) String() string { + return base64.StdEncoding.EncodeToString([]byte(v)) +} + +// encode encodes the BytesValue into an eventstream binary value +// representation. +func (v BytesValue) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + return raw.encodeBytes(w, []byte(v)) +} + +func (v *BytesValue) decode(r io.Reader) error { + buf, err := decodeBytesValue(r) + if err != nil { + return err + } + + *v = BytesValue(buf) + return nil +} + +// An StringValue provides eventstream encoding, and representation of a Go +// string. +type StringValue string + +// Get returns the underlying value. +func (v StringValue) Get() any { + return string(v) +} + +// valueType returns the EventStream header value type value. +func (StringValue) valueType() valueType { + return stringValueType +} + +func (v StringValue) String() string { + return string(v) +} + +// encode encodes the StringValue into an eventstream binary value +// representation. +func (v StringValue) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + return raw.encodeString(w, string(v)) +} + +func (v *StringValue) decode(r io.Reader) error { + s, err := decodeStringValue(r) + if err != nil { + return err + } + + *v = StringValue(s) + return nil +} + +// An TimestampValue provides eventstream encoding, and representation of a Go +// timestamp. +type TimestampValue time.Time + +// Get returns the underlying value. +func (v TimestampValue) Get() any { + return time.Time(v) +} + +// valueType returns the EventStream header value type value. +func (TimestampValue) valueType() valueType { + return timestampValueType +} + +func (v TimestampValue) epochMilli() int64 { + nano := time.Time(v).UnixNano() + msec := nano / int64(time.Millisecond) + return msec +} + +func (v TimestampValue) String() string { + msec := v.epochMilli() + return strconv.FormatInt(msec, 10) +} + +// encode encodes the TimestampValue into an eventstream binary value +// representation. +func (v TimestampValue) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + msec := v.epochMilli() + return raw.encodeScalar(w, msec) +} + +func (v *TimestampValue) decode(r io.Reader) error { + n, err := decodeUint64(r) + if err != nil { + return err + } + + *v = TimestampValue(timeFromEpochMilli(int64(n))) + return nil +} + +// MarshalJSON implements the json.Marshaler interface +func (v TimestampValue) MarshalJSON() ([]byte, error) { + return []byte(v.String()), nil +} + +func timeFromEpochMilli(t int64) time.Time { + secs := t / 1e3 + msec := t % 1e3 + return time.Unix(secs, msec*int64(time.Millisecond)).UTC() +} + +// An UUIDValue provides eventstream encoding, and representation of a UUID +// value. +type UUIDValue [16]byte + +// Get returns the underlying value. +func (v UUIDValue) Get() any { + return v[:] +} + +// valueType returns the EventStream header value type value. +func (UUIDValue) valueType() valueType { + return uuidValueType +} + +func (v UUIDValue) String() string { + var scratch [36]byte + + const dash = '-' + + hex.Encode(scratch[:8], v[0:4]) + scratch[8] = dash + hex.Encode(scratch[9:13], v[4:6]) + scratch[13] = dash + hex.Encode(scratch[14:18], v[6:8]) + scratch[18] = dash + hex.Encode(scratch[19:23], v[8:10]) + scratch[23] = dash + hex.Encode(scratch[24:], v[10:]) + + return string(scratch[:]) +} + +// encode encodes the UUIDValue into an eventstream binary value +// representation. +func (v UUIDValue) encode(w io.Writer) error { + raw := rawValue{ + Type: v.valueType(), + } + + return raw.encodeFixedSlice(w, v[:]) +} + +func (v *UUIDValue) decode(r io.Reader) error { + tv := (*v)[:] + return decodeFixedBytesValue(r, tv) +} diff --git a/eventstream/header_value_test.go b/eventstream/header_value_test.go new file mode 100644 index 000000000..065a9f3d1 --- /dev/null +++ b/eventstream/header_value_test.go @@ -0,0 +1,203 @@ +package eventstream + +import ( + "bytes" + "encoding/binary" + "io" + "reflect" + "testing" + "time" +) + +func binWrite(v any) []byte { + var w bytes.Buffer + binary.Write(&w, binary.BigEndian, v) + return w.Bytes() +} + +var testValueEncodingCases = []struct { + Val Value + Expect []byte + Decode func(io.Reader) (Value, error) +}{ + { + BoolValue(true), + []byte{byte(trueValueType)}, + nil, + }, + { + BoolValue(false), + []byte{byte(falseValueType)}, + nil, + }, + { + Int8Value(0x0f), + []byte{byte(int8ValueType), 0x0f}, + func(r io.Reader) (Value, error) { + var v Int8Value + err := v.decode(r) + return v, err + }, + }, + { + Int16Value(0x0f), + append([]byte{byte(int16ValueType)}, binWrite(int16(0x0f))...), + func(r io.Reader) (Value, error) { + var v Int16Value + err := v.decode(r) + return v, err + }, + }, + { + Int32Value(0x0f), + append([]byte{byte(int32ValueType)}, binWrite(int32(0x0f))...), + func(r io.Reader) (Value, error) { + var v Int32Value + err := v.decode(r) + return v, err + }, + }, + { + Int64Value(0x0f), + append([]byte{byte(int64ValueType)}, binWrite(int64(0x0f))...), + func(r io.Reader) (Value, error) { + var v Int64Value + err := v.decode(r) + return v, err + }, + }, + { + BytesValue([]byte{0, 1, 2, 3}), + []byte{byte(bytesValueType), 0x00, 0x04, 0, 1, 2, 3}, + func(r io.Reader) (Value, error) { + var v BytesValue + err := v.decode(r) + return v, err + }, + }, + { + StringValue("abc123"), + append([]byte{byte(stringValueType), 0, 6}, []byte("abc123")...), + func(r io.Reader) (Value, error) { + var v StringValue + err := v.decode(r) + return v, err + }, + }, + { + TimestampValue( + time.Date(2014, 04, 04, 0, 1, 0, 0, time.FixedZone("PDT", -7)), + ), + append([]byte{byte(timestampValueType)}, binWrite(int64(1396569667000))...), + func(r io.Reader) (Value, error) { + var v TimestampValue + err := v.decode(r) + return v, err + }, + }, + { + UUIDValue( + [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, + ), + []byte{byte(uuidValueType), 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, + func(r io.Reader) (Value, error) { + var v UUIDValue + err := v.decode(r) + return v, err + }, + }, +} + +func TestValue_MarshalValue(t *testing.T) { + for i, c := range testValueEncodingCases { + var w bytes.Buffer + + if err := c.Val.encode(&w); err != nil { + t.Fatalf("%d, expect no error, got %v", i, err) + } + + if e, a := c.Expect, w.Bytes(); !reflect.DeepEqual(e, a) { + t.Errorf("%d, expect %v, got %v", i, e, a) + } + } +} + +func TestHeader_DecodeValues(t *testing.T) { + for i, c := range testValueEncodingCases { + r := bytes.NewBuffer(c.Expect) + v, err := decodeHeaderValue(r) + if err != nil { + t.Fatalf("%d, expect no error, got %v", i, err) + } + + switch tv := v.(type) { + case TimestampValue: + exp := time.Time(c.Val.(TimestampValue)) + if e, a := exp, time.Time(tv); !e.Equal(a) { + t.Errorf("%d, expect %v, got %v", i, e, a) + } + default: + if e, a := c.Val, v; !reflect.DeepEqual(e, a) { + t.Errorf("%d, expect %v, got %v", i, e, a) + } + } + } +} + +func TestValue_Decode(t *testing.T) { + for i, c := range testValueEncodingCases { + if c.Decode == nil { + continue + } + + r := bytes.NewBuffer(c.Expect) + r.ReadByte() // strip off Type field + + v, err := c.Decode(r) + if err != nil { + t.Fatalf("%d, expect no error, got %v", i, err) + } + + switch tv := v.(type) { + case TimestampValue: + exp := time.Time(c.Val.(TimestampValue)) + if e, a := exp, time.Time(tv); !e.Equal(a) { + t.Errorf("%d, expect %v, got %v", i, e, a) + } + default: + if e, a := c.Val, v; !reflect.DeepEqual(e, a) { + t.Errorf("%d, expect %v, got %v", i, e, a) + } + } + } +} + +func TestValue_String(t *testing.T) { + cases := []struct { + Val Value + Expect string + }{ + {BoolValue(true), "true"}, + {BoolValue(false), "false"}, + {Int8Value(0x0f), "0x0f"}, + {Int16Value(0x0f), "0x000f"}, + {Int32Value(0x0f), "0x0000000f"}, + {Int64Value(0x0f), "0x000000000000000f"}, + {BytesValue([]byte{0, 1, 2, 3}), "AAECAw=="}, + {StringValue("abc123"), "abc123"}, + {TimestampValue( + time.Date(2014, 04, 04, 0, 1, 0, 0, time.FixedZone("PDT", -7)), + ), + "1396569667000", + }, + {UUIDValue([16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}), + "00010203-0405-0607-0809-0a0b0c0d0e0f", + }, + } + + for i, c := range cases { + if e, a := c.Expect, c.Val.String(); e != a { + t.Errorf("%d, expect %v, got %v", i, e, a) + } + } +} diff --git a/eventstream/message.go b/eventstream/message.go new file mode 100644 index 000000000..1a77654f7 --- /dev/null +++ b/eventstream/message.go @@ -0,0 +1,99 @@ +package eventstream + +import ( + "bytes" + "encoding/binary" + "hash/crc32" +) + +const preludeLen = 8 +const preludeCRCLen = 4 +const msgCRCLen = 4 +const minMsgLen = preludeLen + preludeCRCLen + msgCRCLen + +var crc32IEEETable = crc32.MakeTable(crc32.IEEE) + +// A Message provides the eventstream message representation. +type Message struct { + Headers Headers + Payload []byte +} + +func (m *Message) rawMessage() (rawMessage, error) { + var raw rawMessage + + if len(m.Headers) > 0 { + var headers bytes.Buffer + if err := EncodeHeaders(&headers, m.Headers); err != nil { + return rawMessage{}, err + } + raw.Headers = headers.Bytes() + raw.HeadersLen = uint32(len(raw.Headers)) + } + + raw.Length = raw.HeadersLen + uint32(len(m.Payload)) + minMsgLen + + hash := crc32.New(crc32IEEETable) + binaryWriteFields(hash, binary.BigEndian, raw.Length, raw.HeadersLen) + raw.PreludeCRC = hash.Sum32() + + binaryWriteFields(hash, binary.BigEndian, raw.PreludeCRC) + + if raw.HeadersLen > 0 { + hash.Write(raw.Headers) + } + + // Read payload bytes and update hash for it as well. + if len(m.Payload) > 0 { + raw.Payload = m.Payload + hash.Write(raw.Payload) + } + + raw.CRC = hash.Sum32() + + return raw, nil +} + +// Clone returns a deep copy of the message. +func (m Message) Clone() Message { + var payload []byte + if m.Payload != nil { + payload = make([]byte, len(m.Payload)) + copy(payload, m.Payload) + } + + return Message{ + Headers: m.Headers.Clone(), + Payload: payload, + } +} + +type messagePrelude struct { + Length uint32 + HeadersLen uint32 + PreludeCRC uint32 +} + +func (p messagePrelude) PayloadLen() uint32 { + return p.Length - p.HeadersLen - minMsgLen +} + +func (p messagePrelude) ValidateLens() error { + if p.Length == 0 { + return LengthError{ + Part: "message prelude", + Want: minMsgLen, + Have: int(p.Length), + } + } + return nil +} + +type rawMessage struct { + messagePrelude + + Headers []byte + Payload []byte + + CRC uint32 +} diff --git a/eventstream/serializer.go b/eventstream/serializer.go new file mode 100644 index 000000000..443951dd2 --- /dev/null +++ b/eventstream/serializer.go @@ -0,0 +1,256 @@ +package eventstream + +import ( + "math/big" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/traits" +) + +// ShapeSerializer wraps a [smithy.ShapeSerializer], much like the internal +// httpbinding serializer, to handle event stream message binding traits. +type ShapeSerializer struct { + Message *Message + + inner smithy.ShapeSerializer + contentType string // may be inflenced by bindings +} + +var _ smithy.ShapeSerializer = (*ShapeSerializer)(nil) + +// NewShapeSerializer returns a serializer for a single Message. +func NewShapeSerializer(msg *Message, inner smithy.ShapeSerializer) *ShapeSerializer { + return &ShapeSerializer{ + Message: msg, + inner: inner, + } +} + +// ContentType returns the resolved content type for the event message payload +// after serialization, which may be affected by bindings. +func (s *ShapeSerializer) ContentType() string { + return s.contentType +} + +// Bytes returns the serialized body bytes. +func (s *ShapeSerializer) Bytes() []byte { + return s.inner.Bytes() +} + +// WriteBool implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), BoolValue(v)) + return + } + s.inner.WriteBool(schema, v) +} + +// WriteBoolPtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBoolPtr(schema *smithy.Schema, v *bool) { + if v != nil { + s.WriteBool(schema, *v) + } +} + +// WriteInt8 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), Int8Value(v)) + return + } + s.inner.WriteInt8(schema, v) +} + +// WriteInt8Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8Ptr(schema *smithy.Schema, v *int8) { + if v != nil { + s.WriteInt8(schema, *v) + } +} + +// WriteInt16 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), Int16Value(v)) + return + } + s.inner.WriteInt16(schema, v) +} + +// WriteInt16Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16Ptr(schema *smithy.Schema, v *int16) { + if v != nil { + s.WriteInt16(schema, *v) + } +} + +// WriteInt32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), Int32Value(v)) + return + } + s.inner.WriteInt32(schema, v) +} + +// WriteInt32Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32Ptr(schema *smithy.Schema, v *int32) { + if v != nil { + s.WriteInt32(schema, *v) + } +} + +// WriteInt64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), Int64Value(v)) + return + } + s.inner.WriteInt64(schema, v) +} + +// WriteInt64Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64Ptr(schema *smithy.Schema, v *int64) { + if v != nil { + s.WriteInt64(schema, *v) + } +} + +// WriteFloat32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { + s.inner.WriteFloat32(schema, v) +} + +// WriteFloat32Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32Ptr(schema *smithy.Schema, v *float32) { + s.inner.WriteFloat32Ptr(schema, v) +} + +// WriteFloat64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { + s.inner.WriteFloat64(schema, v) +} + +// WriteFloat64Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64Ptr(schema *smithy.Schema, v *float64) { + s.inner.WriteFloat64Ptr(schema, v) +} + +// WriteString implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), StringValue(v)) + return + } + if isEventPayload(schema) { + s.Message.Payload = []byte(v) + s.contentType = "text/plain" + return + } + s.inner.WriteString(schema, v) +} + +// WriteStringPtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStringPtr(schema *smithy.Schema, v *string) { + if v != nil { + s.WriteString(schema, *v) + } +} + +// WriteBlob implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), BytesValue(v)) + return + } + if isEventPayload(schema) { + s.Message.Payload = v + s.contentType = "application/octet-stream" + return + } + s.inner.WriteBlob(schema, v) +} + +// WriteTime implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { + if isEventHeader(schema) { + s.Message.Headers.Set(schema.MemberName(), TimestampValue(v)) + return + } + s.inner.WriteTime(schema, v) +} + +// WriteTimePtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { + if v != nil { + s.WriteTime(schema, *v) + } +} + +// WriteStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema, v smithy.Serializable) { + v.Serialize(s) +} + +// WriteUnion implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema, v smithy.Serializable) { + s.inner.WriteUnion(schema, variant, v) +} + +// WriteNil implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteNil(schema *smithy.Schema) { + s.inner.WriteNil(schema) +} + +// WriteList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { + s.inner.WriteList(schema) +} + +// CloseList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseList() { + s.inner.CloseList() +} + +// WriteMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { + s.inner.WriteMap(schema) +} + +// WriteKey implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteKey(schema *smithy.Schema, key string) { + s.inner.WriteKey(schema, key) +} + +// CloseMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseMap() { + s.inner.CloseMap() +} + +// WriteBigInteger implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBigInteger(schema *smithy.Schema, v big.Int) { + s.inner.WriteBigInteger(schema, v) +} + +// WriteBigDecimal implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBigDecimal(schema *smithy.Schema, v big.Float) { + s.inner.WriteBigDecimal(schema, v) +} + +// WriteDocument implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteDocument(schema *smithy.Schema, v document.Value) { + s.inner.WriteDocument(schema, v) +} + +func isEventHeader(schema *smithy.Schema) bool { + _, ok := smithy.SchemaTrait[*traits.EventHeader](schema) + return ok +} + +func isEventPayload(schema *smithy.Schema) bool { + _, ok := smithy.SchemaTrait[*traits.EventPayload](schema) + return ok +} diff --git a/eventstream/shared_test.go b/eventstream/shared_test.go new file mode 100644 index 000000000..09e0f963b --- /dev/null +++ b/eventstream/shared_test.go @@ -0,0 +1,152 @@ +package eventstream + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "path/filepath" + "testing" +) + +type testCase struct { + Name string + Encoded []byte + Decoded decodedMessage +} + +type testErrorCase struct { + Name string + Encoded []byte + Err string +} + +type rawTestCase struct { + Name string + Encoded, Decoded []byte +} + +func readRawTestCases(root, class string) (map[string]rawTestCase, error) { + encoded, err := readTests(filepath.Join(root, "encoded", class)) + if err != nil { + return nil, err + } + + decoded, err := readTests(filepath.Join(root, "decoded", class)) + if err != nil { + return nil, err + } + + if len(encoded) == 0 { + return nil, fmt.Errorf("expect encoded cases, found none") + } + + if len(encoded) != len(decoded) { + return nil, fmt.Errorf("encoded and decoded sets different") + } + + rawCases := map[string]rawTestCase{} + for name, encData := range encoded { + decData, ok := decoded[name] + if !ok { + return nil, fmt.Errorf("encoded %q case not found in decoded set", name) + } + + rawCases[name] = rawTestCase{ + Name: name, + Encoded: encData, + Decoded: decData, + } + } + + return rawCases, nil +} + +func readNegativeTests(root string) ([]testErrorCase, error) { + rawCases, err := readRawTestCases(root, "negative") + if err != nil { + return nil, err + } + + cases := make([]testErrorCase, 0, len(rawCases)) + for name, rawCase := range rawCases { + cases = append(cases, testErrorCase{ + Name: name, + Encoded: rawCase.Encoded, + Err: string(rawCase.Decoded), + }) + } + + return cases, nil +} + +func readPositiveTests(root string) ([]testCase, error) { + rawCases, err := readRawTestCases(root, "positive") + if err != nil { + return nil, err + } + + cases := make([]testCase, 0, len(rawCases)) + for name, rawCase := range rawCases { + + var dec decodedMessage + if err := json.Unmarshal(rawCase.Decoded, &dec); err != nil { + return nil, fmt.Errorf("failed to decode %q, %v", name, err) + } + + cases = append(cases, testCase{ + Name: name, + Encoded: rawCase.Encoded, + Decoded: dec, + }) + } + + return cases, nil +} + +func readTests(root string) (map[string][]byte, error) { + items, err := ioutil.ReadDir(root) + if err != nil { + return nil, fmt.Errorf("failed to read test suite %q dirs, %v", root, err) + } + + cases := map[string][]byte{} + for _, item := range items { + if item.IsDir() { + continue + } + + filename := filepath.Join(root, item.Name()) + data, err := ioutil.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("failed to read test_data file %q, %v", filename, err) + } + + cases[item.Name()] = data + } + + return cases, nil +} + +func compareLines(t *testing.T, a, b []byte) bool { + as := bufio.NewScanner(bytes.NewBuffer(a)) + bs := bufio.NewScanner(bytes.NewBuffer(b)) + + var failed bool + for { + if ab, bb := as.Scan(), bs.Scan(); ab != bb { + t.Errorf("expect a & b to have same number of lines") + return false + } else if !ab { + break + } + + if v1, v2 := as.Text(), bs.Text(); v1 != v2 { + t.Errorf("expect %q to be %q", v1, v2) + failed = true + } + } + + return !failed +} diff --git a/eventstream/signer.go b/eventstream/signer.go new file mode 100644 index 000000000..69f7779d8 --- /dev/null +++ b/eventstream/signer.go @@ -0,0 +1,82 @@ +package eventstream + +import ( + "bytes" + "io" + "time" +) + +// MessageSigner signs event stream message header and payload byte pairs. +// Each invocation chains off the previous signature. +type MessageSigner interface { + SignMessage(headers, payload []byte, signingTime time.Time) ([]byte, error) +} + +// SigningWriter wraps an io.WriteCloser and signs each event stream message +// frame written to it. Each Write call MUST contain exactly one complete +// encoded event stream message frame. +// +// The signing writer wraps each incoming frame in an outer event stream +// message with :date and :chunk-signature headers, then encodes the outer +// message to the underlying writer. +// +// Close sends a signed empty message to signal end-of-stream, then closes +// the underlying writer. +type SigningWriter struct { + writer io.WriteCloser + signer MessageSigner + encoder *Encoder + + headersBuf bytes.Buffer +} + +// NewSigningWriter returns a SigningWriter that signs frames and writes them +// to w. +func NewSigningWriter(w io.WriteCloser, signer MessageSigner) *SigningWriter { + return &SigningWriter{ + writer: w, + signer: signer, + encoder: NewEncoder(), + } +} + +// Write signs a complete event stream message frame and writes the signed +// outer envelope to the underlying writer. +func (s *SigningWriter) Write(frame []byte) (int, error) { + if err := s.signAndWrite(frame); err != nil { + return 0, err + } + return len(frame), nil +} + +// Close sends a signed empty message to signal end-of-stream, then closes +// the underlying writer. +func (s *SigningWriter) Close() error { + if err := s.signAndWrite([]byte{}); err != nil { + _ = s.writer.Close() + return err + } + return s.writer.Close() +} + +func (s *SigningWriter) signAndWrite(payload []byte) error { + now := time.Now().UTC() + + var msg Message + msg.Headers.Set(DateHeader, TimestampValue(now)) + msg.Payload = payload + + s.headersBuf.Reset() + if err := EncodeHeaders(&s.headersBuf, msg.Headers); err != nil { + return err + } + + sig, err := s.signer.SignMessage(s.headersBuf.Bytes(), payload, now) + if err != nil { + return err + } + + msg.Headers.Set(ChunkSignatureHeader, BytesValue(sig)) + + return s.encoder.Encode(s.writer, msg) +} diff --git a/eventstream/testdata/decoded/negative/corrupted_header_len b/eventstream/testdata/decoded/negative/corrupted_header_len new file mode 100644 index 000000000..73e4d6f1f --- /dev/null +++ b/eventstream/testdata/decoded/negative/corrupted_header_len @@ -0,0 +1 @@ +Prelude checksum mismatch \ No newline at end of file diff --git a/eventstream/testdata/decoded/negative/corrupted_headers b/eventstream/testdata/decoded/negative/corrupted_headers new file mode 100644 index 000000000..f56e5c887 --- /dev/null +++ b/eventstream/testdata/decoded/negative/corrupted_headers @@ -0,0 +1 @@ +Message checksum mismatch \ No newline at end of file diff --git a/eventstream/testdata/decoded/negative/corrupted_length b/eventstream/testdata/decoded/negative/corrupted_length new file mode 100644 index 000000000..73e4d6f1f --- /dev/null +++ b/eventstream/testdata/decoded/negative/corrupted_length @@ -0,0 +1 @@ +Prelude checksum mismatch \ No newline at end of file diff --git a/eventstream/testdata/decoded/negative/corrupted_payload b/eventstream/testdata/decoded/negative/corrupted_payload new file mode 100644 index 000000000..f56e5c887 --- /dev/null +++ b/eventstream/testdata/decoded/negative/corrupted_payload @@ -0,0 +1 @@ +Message checksum mismatch \ No newline at end of file diff --git a/eventstream/testdata/decoded/positive/all_headers b/eventstream/testdata/decoded/positive/all_headers new file mode 100644 index 000000000..fd8f96b88 --- /dev/null +++ b/eventstream/testdata/decoded/positive/all_headers @@ -0,0 +1,58 @@ +{ + "total_length": 204, + "headers_length": 175, + "prelude_crc": 263087306, + "headers": [ { + "name": "event-type", + "type": 4, + "value": 40972 + }, + { + "name": "content-type", + "type": 7, + "value": "YXBwbGljYXRpb24vanNvbg==" + }, + { + "name": "bool false", + "type": 1, + "value": false + }, + { + "name": "bool true", + "type": 0, + "value": true + }, + { + "name": "byte", + "type": 2, + "value": -49 + }, + { + "name": "byte buf", + "type": 6, + "value": "SSdtIGEgbGl0dGxlIHRlYXBvdCE=" + }, + { + "name": "timestamp", + "type": 8, + "value": 8675309 + }, + { + "name": "int16", + "type": 3, + "value": 42 + }, + { + "name": "int64", + "type": 5, + "value": 42424242 + }, + { + "name": "uuid", + "type": 9, + "value": "AQIDBAUGBwgJCgsMDQ4PEA==" + } + ], + "payload": "eydmb28nOidiYXInfQ==", + "message_crc": -1415188212 +} diff --git a/eventstream/testdata/decoded/positive/empty_message b/eventstream/testdata/decoded/positive/empty_message new file mode 100644 index 000000000..1d35df8e6 --- /dev/null +++ b/eventstream/testdata/decoded/positive/empty_message @@ -0,0 +1,8 @@ +{ + "total_length": 16, + "headers_length": 0, + "prelude_crc": 96618731, + "headers": [ ], + "payload": "", + "message_crc": 2107164927 +} diff --git a/eventstream/testdata/decoded/positive/int32_header b/eventstream/testdata/decoded/positive/int32_header new file mode 100644 index 000000000..852a0db6e --- /dev/null +++ b/eventstream/testdata/decoded/positive/int32_header @@ -0,0 +1,13 @@ +{ + "total_length": 45, + "headers_length": 16, + "prelude_crc": 1103373496, + "headers": [ { + "name": "event-type", + "type": 4, + "value": 40972 + } + ], + "payload": "eydmb28nOidiYXInfQ==", + "message_crc": 921993376 +} diff --git a/eventstream/testdata/decoded/positive/payload_no_headers b/eventstream/testdata/decoded/positive/payload_no_headers new file mode 100644 index 000000000..1c96631ca --- /dev/null +++ b/eventstream/testdata/decoded/positive/payload_no_headers @@ -0,0 +1,8 @@ +{ + "total_length": 29, + "headers_length": 0, + "prelude_crc": -44921766, + "headers": [ ], + "payload": "eydmb28nOidiYXInfQ==", + "message_crc": -1016776394 +} diff --git a/eventstream/testdata/decoded/positive/payload_one_str_header b/eventstream/testdata/decoded/positive/payload_one_str_header new file mode 100644 index 000000000..e3bfd312f --- /dev/null +++ b/eventstream/testdata/decoded/positive/payload_one_str_header @@ -0,0 +1,13 @@ +{ + "total_length": 61, + "headers_length": 32, + "prelude_crc": 134054806, + "headers": [ { + "name": "content-type", + "type": 7, + "value": "YXBwbGljYXRpb24vanNvbg==" + } + ], + "payload": "eydmb28nOidiYXInfQ==", + "message_crc": -1919153999 +} diff --git a/eventstream/testdata/encoded/negative/corrupted_header_len b/eventstream/testdata/encoded/negative/corrupted_header_len new file mode 100644 index 0000000000000000000000000000000000000000..474929c83bea787b066204a8f83b3dd34a80ca29 GIT binary patch literal 61 zcmZQzV6bIiU{GZL+dPdYIX|x?HLpasq_QBDok1Y6pdcqRIk6-&KTkiaI6tpiJuN?9 Q-AX+vu}HnPcMiu!05M7wF8}}l literal 0 HcmV?d00001 diff --git a/eventstream/testdata/encoded/negative/corrupted_headers b/eventstream/testdata/encoded/negative/corrupted_headers new file mode 100644 index 0000000000000000000000000000000000000000..802a2276c15890de8b9f878eeae079f829145a55 GIT binary patch literal 61 zcmZQzV6bIiU{GNH+dPdYIX|x?HLpasq_QBDok1Y6pdcqRIk6-&KTkiaI6tpCF)cq| Q-AX+vu}HnPcMiu!05r4|XaE2J literal 0 HcmV?d00001 diff --git a/eventstream/testdata/encoded/negative/corrupted_length b/eventstream/testdata/encoded/negative/corrupted_length new file mode 100644 index 0000000000000000000000000000000000000000..4e55a3196fc0ad1b6589657c09de3d01071d5729 GIT binary patch literal 61 zcmZQzV6bCgU{GNH+dPdYIX|x?HLpasq_QBDok1Y6pdcqRIk6-&KTkiaI6tpiJuN?9 Q-AX+vu}HnPcMiu!05MJ!F8}}l literal 0 HcmV?d00001 diff --git a/eventstream/testdata/encoded/negative/corrupted_payload b/eventstream/testdata/encoded/negative/corrupted_payload new file mode 100644 index 0000000000000000000000000000000000000000..2ee9ef3ce1ac8cc3b8afaf579bdb6b38fcaf097e GIT binary patch literal 29 icmZQzV31`1g1Pd-3>a~YcEzJO6$OsVt literal 0 HcmV?d00001 diff --git a/eventstream/testdata/encoded/positive/all_headers b/eventstream/testdata/encoded/positive/all_headers new file mode 100644 index 0000000000000000000000000000000000000000..b986af1489af89dfbb32e59b274c37b307f3ba70 GIT binary patch literal 204 zcmZQzU^v6Tz_6ZwUCJr0)UwpP65W!@f>ag;h6Ox4$@zIDFcEeJfy9D>oXq6JlFa-( z{jB2rJg%ht{2Ya}#GK+(Mouuhq^LBNfhDQ3B$eqr2Z&NgDotZ!5b;#cRY+9G$t)?! zNmVFGO)SVSQRFPi%uOvWNz5(a06Ktyp(Xw;Yi3@Fp&2uS7KmYH!U_^*Y7gJUQd*js n!pX?Q%)-jX&cVsW&BM#bFHo(XmY=U~rJj^nq+Yvv=|>&_SX(=j literal 0 HcmV?d00001 diff --git a/eventstream/testdata/encoded/positive/empty_message b/eventstream/testdata/encoded/positive/empty_message new file mode 100644 index 0000000000000000000000000000000000000000..663632857e51765759693d08ec460cea9e60a44d GIT binary patch literal 16 WcmZQzU=Uyc0@gzwuWM(V_zwUQ=mh5g literal 0 HcmV?d00001 diff --git a/eventstream/testdata/encoded/positive/int32_header b/eventstream/testdata/encoded/positive/int32_header new file mode 100644 index 0000000000000000000000000000000000000000..4e13b503dbb54f329ed22126b6c35961bd820ba6 GIT binary patch literal 45 zcmZQzV9;e?U=VOTqOyZ4wJbHSM7N}}AeDuIVF6FIdRl(Ix|MoTVv%~S*_Va|0RGes AdjJ3c literal 0 HcmV?d00001 diff --git a/eventstream/testdata/encoded/positive/payload_no_headers b/eventstream/testdata/encoded/positive/payload_no_headers new file mode 100644 index 0000000000000000000000000000000000000000..47733a111893568d229a73bfef3be6ba93db48aa GIT binary patch literal 29 icmZQzV31`1g1Pd-3>a~YcEzJO8=m;?Y literal 0 HcmV?d00001 diff --git a/eventstream/testdata/encoded/positive/payload_one_str_header b/eventstream/testdata/encoded/positive/payload_one_str_header new file mode 100644 index 0000000000000000000000000000000000000000..d4abaa7f8b4eb25c7683940c779c314902aae08e GIT binary patch literal 61 zcmZQzV6bIiU{GNH+dPdYIX|x?HLpasq_QBDok1Y6pdcqRIk6-&KTkiaI6tpiJuN?9 Q-AX+vu}HnPcMiu!05KF4E&u=k literal 0 HcmV?d00001 diff --git a/schema.go b/schema.go index 926653869..9e1fb3eb0 100644 --- a/schema.go +++ b/schema.go @@ -4,6 +4,8 @@ import ( "fmt" "maps" "strings" + + "github.com/aws/smithy-go/traits" ) // ShapeType is a type of Smithy shape. @@ -44,7 +46,7 @@ type ShapeID struct { } // String returns the IDL microformat for the shape ID. -func (s *ShapeID) String() string { +func (s ShapeID) String() string { if s.Member == "" { return fmt.Sprintf("%s#%s", s.Namespace, s.Name) } @@ -62,10 +64,11 @@ func stoid(s string) ShapeID { // Generated clients use schemas at runtime to dynamically (de)serialize // request/responses. type Schema struct { - id ShapeID - typ ShapeType - members map[string]*Schema // member name -> schema - traits map[string]Trait // trait ID -> trait + id ShapeID + typ ShapeType + members map[string]*Schema // member name -> schema + traits map[string]Trait // trait ID -> trait + targetID ShapeID // for member schemas, the target's shape ID listMember *Schema mapKey, mapValue *Schema @@ -94,6 +97,7 @@ func (s *Schema) AddMember(name string, target *Schema, traits ...Trait) *Schema typ: target.typ, members: target.members, traits: maps.Clone(target.traits), + targetID: target.id, listMember: target.listMember, mapKey: target.mapKey, mapValue: target.mapValue, @@ -138,6 +142,11 @@ func (s *Schema) MemberName() string { return s.id.Member } +// TargetID returns the shape ID of the member's target shape. +func (s *Schema) TargetID() ShapeID { + return s.targetID +} + // Type returns the shape type of the schema. func (s *Schema) Type() ShapeType { return s.typ @@ -153,6 +162,36 @@ func (s *Schema) Members() map[string]*Schema { return s.members } +// OperationSchema describes an operation, which is essentially its own schema +// with additional pointers to its input and output. +type OperationSchema struct { + *Schema + Input, Output *Schema + + inputStream, outputStream bool +} + +// NewOperationSchema returns an OperationSchema for (input, output). +func NewOperationSchema(op, input, output *Schema) *OperationSchema { + return &OperationSchema{ + Schema: op, + Input: input, + Output: output, + inputStream: isEventStream(input), + outputStream: isEventStream(output), + } +} + +// IsInputEventStream reports whether this is an input event stream. +func (s *OperationSchema) IsInputEventStream() bool { + return s.inputStream +} + +// IsOutputEventStream reports whether this is an output event stream. +func (s *OperationSchema) IsOutputEventStream() bool { + return s.outputStream +} + // Trait returns the target trait on the schema if it exists. func SchemaTrait[T Trait](s *Schema) (T, bool) { var trait T @@ -169,3 +208,15 @@ func SchemaTrait[T Trait](s *Schema) (T, bool) { tt, ok := opaque.(T) return tt, ok } + +func isEventStream(s *Schema) bool { + for _, m := range s.members { + if m.typ != ShapeTypeUnion { + continue + } + if _, ok := SchemaTrait[*traits.Streaming](m); ok { + return true + } + } + return false +} diff --git a/transport/http/auth.go b/transport/http/auth.go index 58e1ab5ef..5b5adad0b 100644 --- a/transport/http/auth.go +++ b/transport/http/auth.go @@ -5,6 +5,7 @@ import ( smithy "github.com/aws/smithy-go" "github.com/aws/smithy-go/auth" + "github.com/aws/smithy-go/eventstream" ) // AuthScheme defines an HTTP authentication scheme. @@ -19,3 +20,11 @@ type AuthScheme interface { type Signer interface { SignRequest(context.Context, *Request, auth.Identity, smithy.Properties) error } + +// EventStreamSigner is an optional interface that a [Signer] can implement to +// support signing of event stream messages. If the resolved auth scheme's +// signer implements this interface, the event stream middleware will use it to +// wrap the outbound message stream with a signing layer. +type EventStreamSigner interface { + NewMessageSigner(ctx context.Context, r *Request, identity auth.Identity, props smithy.Properties) (eventstream.MessageSigner, error) +} diff --git a/transport/http/eventstream.go b/transport/http/eventstream.go new file mode 100644 index 000000000..251db8ac3 --- /dev/null +++ b/transport/http/eventstream.go @@ -0,0 +1,209 @@ +package http + +import ( + "context" + "fmt" + "io" + "sync" + + "github.com/aws/smithy-go" + smithysync "github.com/aws/smithy-go/sync" +) + +// EventStreamWriter writes events to a stream using a ClientProtocol. +// +// The writer manages a background goroutine that facilitates the write loop. +// Calls to Send() on a writer will block until the message has been written. +// +// The writer doesn't know anything about signing. If event stream messages are +// getting signed by the client then the underlying io.Writer has already been +// wrapped to handle that at this point. +type EventStreamWriter struct { + protocol ClientProtocol + schema *smithy.Schema + + eventStream io.WriteCloser + stream chan singleflight + done chan struct{} + err *smithysync.OnceErr + + closeOnce sync.Once +} + +// we send one message at a time, the underlying write loop marshals these into +// the writer and reports back any error to the error channel +type singleflight struct { + variant *smithy.Schema + event smithy.Serializable + errCh chan<- error +} + +// NewEventStreamWriter returns an EventStreamWriter for the given schema. +func NewEventStreamWriter(protocol ClientProtocol, schema *smithy.Schema, stream io.WriteCloser) *EventStreamWriter { + w := &EventStreamWriter{ + protocol: protocol, + schema: schema, + + eventStream: stream, + stream: make(chan singleflight), + done: make(chan struct{}), + err: smithysync.NewOnceErr(), + } + + go w.writeStream() + + return w +} + +func (w *EventStreamWriter) writeStream() { + defer w.Close() + + for { + select { + case ev := <-w.stream: + err := w.protocol.SerializeEventMessage(w.schema, ev.variant, ev.event, w.eventStream) + if err != nil { + w.err.SetError(err) + } + ev.errCh <- err + case <-w.done: + return + } + } +} + +// Send writes a single event to the stream. +func (w *EventStreamWriter) Send(ctx context.Context, variant *smithy.Schema, event smithy.Serializable) error { + if err := w.err.Err(); err != nil { + return err + } + + errCh := make(chan error, 1) + select { + case w.stream <- singleflight{variant, event, errCh}: + case <-ctx.Done(): + return ctx.Err() + case <-w.done: + return fmt.Errorf("stream closed, unable to send event") + } + + select { + case err := <-errCh: + return err + case <-ctx.Done(): + return ctx.Err() + case <-w.done: + return fmt.Errorf("stream closed, unable to send event") + } +} + +// Close signals end-of-stream and closes the underlying writer. Close is +// safe for concurrent calls. +func (w *EventStreamWriter) Close() error { + w.closeOnce.Do(func() { + close(w.done) + w.err.SetError(w.eventStream.Close()) + }) + return w.err.Err() +} + +// Err returns the first error encountered during writing. +func (w *EventStreamWriter) Err() error { + return w.err.Err() +} + +// ErrorSet returns a channel that is closed when an error occurs. +func (w *EventStreamWriter) ErrorSet() <-chan struct{} { + return w.err.ErrorSet() +} + +// EventStreamReader reads events from a stream using a ClientProtocol. +type EventStreamReader struct { + protocol ClientProtocol + schema *smithy.Schema + types *smithy.TypeRegistry + + eventStream io.ReadCloser + stream chan smithy.Deserializable + done chan struct{} + err *smithysync.OnceErr + + closeOnce sync.Once +} + +// NewEventStreamReader returns an EventStreamReader that deserializes events +// through the given protocol from r. The schema is the event stream union +// schema. +func NewEventStreamReader(protocol ClientProtocol, schema *smithy.Schema, types *smithy.TypeRegistry, stream io.ReadCloser) *EventStreamReader { + r := &EventStreamReader{ + protocol: protocol, + schema: schema, + types: types, + + eventStream: stream, + stream: make(chan smithy.Deserializable), + done: make(chan struct{}), + err: smithysync.NewOnceErr(), + } + + go r.readEventStream() + + return r +} + +func (r *EventStreamReader) readEventStream() { + defer r.Close() + defer close(r.stream) + + for { + event, err := r.protocol.DeserializeEventMessage(r.schema, r.types, r.eventStream) + if err != nil { + if err == io.EOF { + return + } + select { + case <-r.done: + return + default: + r.err.SetError(err) + return + } + } + + select { + case r.stream <- event: + case <-r.done: + return + } + } +} + +// Events returns the channel from which deserialized events can be read. +func (r *EventStreamReader) Events() <-chan smithy.Deserializable { + return r.stream +} + +// Close stops the reader and releases the underlying stream. Close is safe +// for concurrent calls. +func (r *EventStreamReader) Close() error { + r.closeOnce.Do(func() { + close(r.done) + r.eventStream.Close() + }) + return r.err.Err() +} + +// Err returns the first error encountered during reading. +func (r *EventStreamReader) Err() error { + return r.err.Err() +} + +// ErrorSet returns a channel that is closed when an error occurs. +func (r *EventStreamReader) ErrorSet() <-chan struct{} { + return r.err.ErrorSet() +} + +// Closed returns a channel that is closed when the reader is closed. +func (r *EventStreamReader) Closed() <-chan struct{} { + return r.done +} diff --git a/transport/http/eventstream_middleware.go b/transport/http/eventstream_middleware.go new file mode 100644 index 000000000..f7d60dc76 --- /dev/null +++ b/transport/http/eventstream_middleware.go @@ -0,0 +1,69 @@ +package http + +import ( + "context" + "fmt" + "io" + + "github.com/aws/smithy-go/middleware" +) + +type eventStreamWriterKey struct{} + +// GetInputStreamWriter returns the io.WriteCloser pipe used for the +// operation's input event stream. +func GetInputStreamWriter(ctx context.Context) io.WriteCloser { + writeCloser, _ := middleware.GetStackValue(ctx, eventStreamWriterKey{}).(io.WriteCloser) + return writeCloser +} + +func setInputStreamWriter(ctx context.Context, writeCloser io.WriteCloser) context.Context { + return middleware.WithStackValue(ctx, eventStreamWriterKey{}, writeCloser) +} + +// InitializeStreamWriter is a Finalize middleware that creates an in-memory +// pipe and sets it as the HTTP request body so event stream messages can be +// written after the request is sent. +type InitializeStreamWriter struct{} + +// AddInitializeStreamWriter adds the InitializeStreamWriter middleware to the +// provided stack. +func AddInitializeStreamWriter(stack *middleware.Stack) error { + return stack.Finalize.Add(&InitializeStreamWriter{}, middleware.After) +} + +// ID returns the identifier for the middleware. +func (i *InitializeStreamWriter) ID() string { + return "InitializeStreamWriter" +} + +// HandleFinalize is the middleware implementation. +func (i *InitializeStreamWriter) HandleFinalize( + ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler, +) ( + out middleware.FinalizeOutput, metadata middleware.Metadata, err error, +) { + request, ok := in.Request.(*Request) + if !ok { + return out, metadata, fmt.Errorf("unknown transport type: %T", in.Request) + } + + inputReader, inputWriter := io.Pipe() + defer func() { + if err == nil { + return + } + _ = inputReader.Close() + _ = inputWriter.Close() + }() + + request, err = request.SetStream(inputReader) + if err != nil { + return out, metadata, err + } + in.Request = request + + ctx = setInputStreamWriter(ctx, inputWriter) + + return next.HandleFinalize(ctx, in) +} diff --git a/transport/http/protocol.go b/transport/http/protocol.go index 3d3e5bca7..db741c542 100644 --- a/transport/http/protocol.go +++ b/transport/http/protocol.go @@ -2,6 +2,7 @@ package http import ( "context" + "io" "github.com/aws/smithy-go" ) @@ -14,6 +15,13 @@ import ( // protocols implemented as part of the Smithy client runtime. type ClientProtocol interface { ID() string - SerializeRequest(context.Context, *smithy.Schema, smithy.Serializable, *Request) error - DeserializeResponse(ctx context.Context, schema *smithy.Schema, types *smithy.TypeRegistry, resp *Response, out smithy.Deserializable) error -} + SerializeRequest(context.Context, *smithy.OperationSchema, smithy.Serializable, *Request) error + DeserializeResponse(ctx context.Context, schema *smithy.OperationSchema, types *smithy.TypeRegistry, resp *Response, out smithy.Deserializable) error + + // event stream APIs + HasInitialEventMessage() bool + SerializeEventMessage(schema, variant *smithy.Schema, v smithy.Serializable, w io.Writer) error + DeserializeEventMessage(schema *smithy.Schema, types *smithy.TypeRegistry, r io.Reader) (smithy.Deserializable, error) + SerializeInitialRequest(schema *smithy.Schema, v smithy.Serializable, w io.Writer) error + DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, out smithy.Deserializable) error +} \ No newline at end of file From 58d7f87e3798dc3551794b4e177fd46110db34eb Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Tue, 14 Apr 2026 13:57:00 -0400 Subject: [PATCH 08/38] implement rpcv2cbor (#646) * implement rpcv2cbor * Update aws-protocols/internal/json/shape_deserializer.go Co-authored-by: Luis Madrigal <599908+Madrigal@users.noreply.github.com> * Update codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java Co-authored-by: Luis Madrigal <599908+Madrigal@users.noreply.github.com> * remove explicit namespace junk * error * nit --------- Co-authored-by: Luis Madrigal <599908+Madrigal@users.noreply.github.com> --- aws-protocols/awsjson10/awsjson10.go | 1 + .../internal/json/shape_deserializer.go | 55 +- .../smithy/go/codegen/CodegenVisitor.java | 38 +- .../go/codegen/DefaultTraitGenerators.java | 4 + .../smithy/go/codegen/ServiceGenerator.java | 20 +- .../smithy/go/codegen/SmithyGoDependency.java | 3 + .../go/codegen/serde2/UnionDeserializer.java | 15 +- internal/errors/errors.go | 56 ++ serde.go | 26 + smithy-http-protocols/go.mod | 7 + smithy-http-protocols/go.sum | 0 smithy-http-protocols/internal/cbor/codec.go | 20 + .../internal/cbor/float16.go | 45 + .../internal/cbor/shape_deserializer.go | 785 ++++++++++++++++++ .../internal/cbor/shape_serializer.go | 456 ++++++++++ smithy-http-protocols/rpcv2/rpcv2.go | 232 ++++++ traits/traits.go | 8 + 17 files changed, 1743 insertions(+), 28 deletions(-) create mode 100644 internal/errors/errors.go create mode 100644 smithy-http-protocols/go.mod create mode 100644 smithy-http-protocols/go.sum create mode 100644 smithy-http-protocols/internal/cbor/codec.go create mode 100644 smithy-http-protocols/internal/cbor/float16.go create mode 100644 smithy-http-protocols/internal/cbor/shape_deserializer.go create mode 100644 smithy-http-protocols/internal/cbor/shape_serializer.go create mode 100644 smithy-http-protocols/rpcv2/rpcv2.go diff --git a/aws-protocols/awsjson10/awsjson10.go b/aws-protocols/awsjson10/awsjson10.go index ae0cb4ebc..d3cd51037 100644 --- a/aws-protocols/awsjson10/awsjson10.go +++ b/aws-protocols/awsjson10/awsjson10.go @@ -30,6 +30,7 @@ func New() *Protocol { // New11 returns an instance of the awsJson 1.1 protocol. // // TODO(serde2): figure out how we want to organize awsjson10 vs 11 +// TODO(serde2): this doesn't do query compatible yet func New11() *Protocol { return &Protocol{ version: "1.1", diff --git a/aws-protocols/internal/json/shape_deserializer.go b/aws-protocols/internal/json/shape_deserializer.go index 5ca6a4060..e3e050757 100644 --- a/aws-protocols/internal/json/shape_deserializer.go +++ b/aws-protocols/internal/json/shape_deserializer.go @@ -453,22 +453,53 @@ func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { return member, nil } +type unionCtx struct { + schema *smithy.Schema +} + // ReadUnion implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) { - // Decode the entire union object to find the non-null member. - var raw map[string]json.RawMessage - if err := d.dec.Decode(&raw); err != nil { - return nil, fmt.Errorf("decode union: %w", err) + if _, ok := d.head.Top().(*unionCtx); !ok { + if isNil, err := d.ReadNil(s); isNil || err != nil { + return nil, err + } + + tok, err := d.token() + if err != nil { + return nil, err + } + delim, ok := tok.(json.Delim) + if !ok || delim != '{' { + return nil, fmt.Errorf("expected '{', got %v", tok) + } + d.head.Push(&unionCtx{schema: s}) } - for key, val := range raw { - if string(val) == "null" { + for d.more() { + tok, err := d.token() + if err != nil { + return nil, err + } + key, ok := tok.(string) + if !ok { + return nil, fmt.Errorf("expected string key, got %T", tok) + } + + // skip null values + isNil, err := d.ReadNil(nil) + if err != nil { + return nil, err + } else { + continue + } + if err != nil { + return nil, err + } continue } member := s.Member(key) if member == nil && d.opts.UseJSONName { - // Try matching by jsonName trait for _, m := range s.Members() { if jn, ok := smithy.SchemaTrait[*traits.JSONName](m); ok && jn.Name == key { member = m @@ -477,17 +508,17 @@ func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) } } if member == nil { + if err := d.skip(); err != nil { + return nil, err + } continue } - // Replace the decoder with one that reads just this value - dec := json.NewDecoder(bytes.NewReader(val)) - dec.UseNumber() - d.dec = dec return member, nil } - return nil, nil + d.head.Pop() + return nil, d.expectDelim('}') } // used to skip over a struct member that we didn't have a schema for, though diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java index 9eef82873..a2a6365c2 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -297,7 +298,22 @@ void execute() { writer.write(""); writer.writeDocs("Initialize schema members after all schemas are declared to avoid initialization cycles"); writer.openBlock("func init() {", "}", () -> { - for (Shape shape : shapes) { + // Topologically sort shapes so that target schemas have their + // members initialized before they are used as targets in + // AddMember calls. This ensures MapKey(), MapValue(), and + // ListMember() are non-nil when the member schema is created. + var shapeIds = new HashSet(); + for (Shape s : shapes) { + shapeIds.add(s.getId()); + } + + var sorted = new ArrayList(); + var visited = new HashSet(); + for (Shape s : shapes) { + topoVisit(s, model, shapeIds, visited, sorted); + } + + for (Shape shape : sorted) { new SchemaGenerator(ctx, shape).acceptMembersInit(writer); } }); @@ -497,4 +513,24 @@ public Void intEnumShape(IntEnumShape shape) { writers.useShapeWriter(shape, writer -> new IntEnumGenerator(symbolProvider, writer, shape).run()); return null; } + + private static void topoVisit( + Shape shape, + Model model, + Set inSet, + Set visited, + List result + ) { + if (visited.contains(shape.getId())) { + return; + } + visited.add(shape.getId()); + for (var member : shape.members()) { + var targetId = member.getTarget(); + if (inSet.contains(targetId)) { + topoVisit(model.expectShape(targetId), model, inSet, visited, result); + } + } + result.add(shape); + } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java index c1bba9e6c..1eedc167f 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Map; import software.amazon.smithy.aws.traits.protocols.AwsQueryErrorTrait; +import software.amazon.smithy.go.codegen.trait.BackfilledInputOutputTrait; import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.traits.EventHeaderTrait; import software.amazon.smithy.model.traits.EventPayloadTrait; @@ -82,6 +83,9 @@ public class DefaultTraitGenerators { GENERATORS.put(AwsQueryErrorTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("AWSQueryError"), "ErrorCode", AwsQueryErrorTrait::getCode, "StatusCode", AwsQueryErrorTrait::getHttpResponseCode)); + + // Codegen-internal traits + GENERATORS.put(BackfilledInputOutputTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("UnitShape"))); } public static TraitGenerator forTrait(ShapeId id) { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java index 861e10712..dbbdebe73 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java @@ -30,6 +30,7 @@ import java.util.stream.Stream; import software.amazon.smithy.aws.traits.protocols.AwsJson1_0Trait; import software.amazon.smithy.aws.traits.protocols.AwsJson1_1Trait; +import software.amazon.smithy.aws.traits.protocols.AwsQueryCompatibleTrait; import software.amazon.smithy.aws.traits.protocols.RestJson1Trait; import software.amazon.smithy.codegen.core.CodegenException; import software.amazon.smithy.codegen.core.Symbol; @@ -46,12 +47,12 @@ import software.amazon.smithy.go.codegen.integration.GoIntegration; import software.amazon.smithy.go.codegen.integration.OperationMetricsStruct; import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin; -import software.amazon.smithy.go.codegen.SmithyGoDependency; import software.amazon.smithy.model.Model; import software.amazon.smithy.model.knowledge.ServiceIndex; import software.amazon.smithy.model.knowledge.TopDownIndex; import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.protocol.traits.Rpcv2CborTrait; import software.amazon.smithy.utils.MapUtils; /** @@ -261,10 +262,10 @@ func New(options $options:L, optFns ...func(*$options:L)) *$client:L { private Writable generateExperimentalSerdeResolvers() { ensureSupportedProtocol(); // TODO(serde2) dynamically resolve a symbol based on traits - return goTemplate("options.Protocol = $T()", resolveDefaultProtocol()); + return goTemplate("options.Protocol = $W", resolveDefaultProtocol()); } - private Symbol resolveDefaultProtocol() { + private Writable resolveDefaultProtocol() { Set protocols = ServiceIndex.of(model).getProtocols(service).keySet(); var preferred = PROTOCOLS_BY_PRIORITY.stream() .filter(protocols::contains) @@ -272,11 +273,18 @@ private Symbol resolveDefaultProtocol() { .get(); if (preferred.equals(AwsJson1_0Trait.ID)) { - return SmithyGoDependency.SMITHY_AWS_PROTOCOLS_JSON10.func("New"); + return goTemplate("$T()", SmithyGoDependency.SMITHY_AWS_PROTOCOLS_JSON10.func("New")); } else if (preferred.equals(AwsJson1_1Trait.ID)) { - return SmithyGoDependency.SMITHY_AWS_PROTOCOLS_JSON10.func("New11"); + return goTemplate("$T()", SmithyGoDependency.SMITHY_AWS_PROTOCOLS_JSON10.func("New11")); } else if (preferred.equals(RestJson1Trait.ID)) { - return SmithyGoDependency.SMITHY_AWS_PROTOCOLS_RESTJSON1.func("New"); + return goTemplate("$T()", SmithyGoDependency.SMITHY_AWS_PROTOCOLS_RESTJSON1.func("New")); + } else if (preferred.equals(Rpcv2CborTrait.ID)) { + return goTemplate("$T($W)", + SmithyGoDependency.SMITHY_HTTP_PROTOCOLS_RPCV2.func("NewCBOR"), + service.hasTrait(AwsQueryCompatibleTrait.class) + ? goTemplate("$T", SmithyGoDependency.SMITHY_HTTP_PROTOCOLS_RPCV2.valueSymbol("UseQueryCompatible")) + : emptyGoTemplate() + ); } else { throw new CodegenException("unsupported schema-serde protocol " + preferred); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java index d31f0c42c..5002c43fe 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java @@ -86,6 +86,9 @@ public final class SmithyGoDependency { public static final GoDependency SMITHY_AWS_PROTOCOLS_JSON10 = relativePackage( "github.com/aws/smithy-go/aws-protocols", "awsjson10", "v1.0.0", null); public static final GoDependency SMITHY_AWS_PROTOCOLS_RESTJSON1 = relativePackage( "github.com/aws/smithy-go/aws-protocols", "restjson1", "v1.0.0", null); + public static final GoDependency SMITHY_HTTP_PROTOCOLS = relativePackage( "github.com/aws/smithy-go/smithy-http-protocols", null, "v1.0.0", null); + public static final GoDependency SMITHY_HTTP_PROTOCOLS_RPCV2 = relativePackage( "github.com/aws/smithy-go/smithy-http-protocols", "rpcv2", "v1.0.0", null); + public static final GoDependency MATH = stdlib("math"); private static final String SMITHY_SOURCE_PATH = "github.com/aws/smithy-go"; diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java index 3c35b6e84..e85d12970 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java @@ -33,15 +33,12 @@ public void accept(GoWriter writer) { writer.writeGoTemplate(""" func deserialize$shapeName:L(d smithy.ShapeDeserializer, s *smithy.Schema, v *$symbol:T) error { - ms, err := d.ReadUnion(s) - if err != nil { - return err - } - - switch ms { - $cases:W - } - return nil + return smithy.ReadUnion(d, s, func(ms *smithy.Schema) error { + switch ms { + $cases:W + } + return nil + }) } """, Map.of( "shapeName", shape.getId().getName(), diff --git a/internal/errors/errors.go b/internal/errors/errors.go new file mode 100644 index 000000000..5eea7b798 --- /dev/null +++ b/internal/errors/errors.go @@ -0,0 +1,56 @@ +package errors + +import ( + "reflect" + "strings" + + "github.com/aws/smithy-go" +) + +// SetErrorCodeOverride sets the ErrorCodeOverride field on err if present. +// +// This is used by protocols in awsquery-compatible mode that need to override +// the error code on a deserialized error. +func SetErrorCodeOverride(err error, code string) { + // yes it's reflection but i don't really view errors as a hot path so i + // think it's fine + v := reflect.ValueOf(err) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + if v.Kind() != reflect.Struct { + return + } + + f := v.FieldByName("ErrorCodeOverride") + if !f.IsValid() || !f.CanSet() || f.Type() != reflect.TypeOf((*string)(nil)) { + return + } + + f.Set(reflect.ValueOf(&code)) +} + +// SanitizeErrorCode strips namespace prefixes and colon suffixes from protocol +// error codes. +func SanitizeErrorCode(code string) string { + _, noprefix, ok := strings.Cut(code, "#") + if !ok { + noprefix = code // If sep does not appear in s, cut returns s, "", false. + } + + code, _, _ = strings.Cut(noprefix, ":") + return code +} + +// ParseQueryError extracts the error code and fault from X-Amzn-Query-Error. +func ParseQueryError(header string) (string, smithy.ErrorFault) { + code, fault, _ := strings.Cut(header, ";") + switch fault { + case "Sender": + return code, smithy.FaultClient + case "Receiver": + return code, smithy.FaultServer + default: + return code, smithy.FaultUnknown + } +} diff --git a/serde.go b/serde.go index f9320ba08..c0fb6d6ab 100644 --- a/serde.go +++ b/serde.go @@ -1,6 +1,7 @@ package smithy import ( + "fmt" "io" "math/big" "time" @@ -158,6 +159,31 @@ type DeserializableError interface { error } +// ReadUnion is a utility API for generated clients. +func ReadUnion(d ShapeDeserializer, schema *Schema, memberFn func(*Schema) error) error { + ms, err := d.ReadUnion(schema) + if err != nil { + return err + } + + if ms != nil { + if err := memberFn(ms); err != nil { + return err + } + } + + for { + ms, err = d.ReadUnion(schema) + if err != nil { + return err + } + if ms == nil { + return nil + } + return fmt.Errorf("union has more than one non-nil member: %s", ms.MemberName()) + } +} + // ReadStruct is a utility API for generated clients. func ReadStruct(d ShapeDeserializer, schema *Schema, memberFn func(*Schema) error) error { if err := d.ReadStruct(schema); err != nil { diff --git a/smithy-http-protocols/go.mod b/smithy-http-protocols/go.mod new file mode 100644 index 000000000..56edb0c58 --- /dev/null +++ b/smithy-http-protocols/go.mod @@ -0,0 +1,7 @@ +module github.com/aws/smithy-go/smithy-http-protocols + +go 1.24 + +require github.com/aws/smithy-go v1.24.0 + +replace github.com/aws/smithy-go => ../ diff --git a/smithy-http-protocols/go.sum b/smithy-http-protocols/go.sum new file mode 100644 index 000000000..e69de29bb diff --git a/smithy-http-protocols/internal/cbor/codec.go b/smithy-http-protocols/internal/cbor/codec.go new file mode 100644 index 000000000..d07c6c087 --- /dev/null +++ b/smithy-http-protocols/internal/cbor/codec.go @@ -0,0 +1,20 @@ +package cbor + +import ( + "github.com/aws/smithy-go" +) + +// Codec is a CBOR codec. +type Codec struct{} + +var _ smithy.Codec = (*Codec)(nil) + +// Serializer returns a CBOR shape serializer. +func (c *Codec) Serializer() smithy.ShapeSerializer { + return NewShapeSerializer() +} + +// Deserializer returns a CBOR shape deserializer. +func (c *Codec) Deserializer(p []byte) smithy.ShapeDeserializer { + return NewShapeDeserializer(p) +} diff --git a/smithy-http-protocols/internal/cbor/float16.go b/smithy-http-protocols/internal/cbor/float16.go new file mode 100644 index 000000000..081eea705 --- /dev/null +++ b/smithy-http-protocols/internal/cbor/float16.go @@ -0,0 +1,45 @@ +package cbor + +func float16to32(f uint16) uint32 { + sign, exp, mant := splitf16(f) + if exp == 0x1f { + return sign | 0xff<<23 | mant // infinity/NaN + } + + if exp == 0 { // subnormal + if mant == 0 { + return sign + } + return normalize(sign, mant) + } + + return sign | (exp+127-15)<<23 | mant // rebias exp by the difference between the two +} + +func splitf16(f uint16) (sign, exp, mantissa uint32) { + const smask = 0x1 << 15 // put sign in float32 position + const emask = 0x1f << 10 // pull exponent as a number (for bias shift) + const mmask = 0x3ff // put mantissa in float32 position + + return uint32(f&smask) << 16, uint32(f&emask) >> 10, uint32(f&mmask) << 13 +} + +// moves a float16 normal into normal float32 space +// to do this we must re-express the float16 mantissa in terms of a normal +// float32 where the hidden bit is 1, e.g. +// +// f16: 0 00000 0001010000 = 0.000101 * 2^(-14), which is equal to +// f32: 0 01101101 01000000000000000000000 = 1.01 * 2^(-18) +// +// this is achieved by shifting the mantissa to the right until the leading bit +// that == 1 reaches position 24, then the number of positions shifted over is +// equal to the offset from the subnormal exponent +func normalize(sign, mant uint32) uint32 { + exp := uint32(-14 + 127) // f16 subnormal exp, with f32 bias + for mant&0x800000 == 0 { // repeat until bit 24 ("hidden" mantissa) is 1 + mant <<= 1 + exp-- // tracking the offset + } + mant &= 0x7fffff // remask to 23bit + return sign | exp<<23 | mant +} diff --git a/smithy-http-protocols/internal/cbor/shape_deserializer.go b/smithy-http-protocols/internal/cbor/shape_deserializer.go new file mode 100644 index 000000000..4c08a6090 --- /dev/null +++ b/smithy-http-protocols/internal/cbor/shape_deserializer.go @@ -0,0 +1,785 @@ +package cbor + +import ( + "encoding/binary" + "errors" + "fmt" + "math" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + smithycbor "github.com/aws/smithy-go/encoding/cbor" +) + +var errUnexpectedEOF = errors.New("unexpected end of CBOR data") + +// ShapeDeserializer implements unmarshaling of CBOR into Smithy shapes. +type ShapeDeserializer struct { + p []byte + off int + head deserStack + opts ShapeDeserializerOptions +} + +type deserCtxKind byte + +const ( + deserCtxList deserCtxKind = iota + deserCtxMap + deserCtxStruct + deserCtxUnion +) + +type deserCtx struct { + kind deserCtxKind + schema *smithy.Schema + remaining int +} + +type deserStack struct { + values []deserCtx +} + +func (s *deserStack) top() *deserCtx { + if len(s.values) == 0 { + return nil + } + return &s.values[len(s.values)-1] +} + +func (s *deserStack) push(v deserCtx) { + s.values = append(s.values, v) +} + +func (s *deserStack) pop() { + s.values = s.values[:len(s.values)-1] +} + +// ShapeDeserializerOptions configures ShapeDeserializer. +type ShapeDeserializerOptions struct{} + +var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil) + +// NewShapeDeserializer creates a new ShapeDeserializer. +func NewShapeDeserializer(p []byte, opts ...func(*ShapeDeserializerOptions)) *ShapeDeserializer { + o := ShapeDeserializerOptions{} + for _, fn := range opts { + fn(&o) + } + return &ShapeDeserializer{p: p, opts: o} +} + +func (d *ShapeDeserializer) eof() bool { + return d.off >= len(d.p) +} + +func (d *ShapeDeserializer) peekMajor() majorType { + return majorType(d.p[d.off] & 0xe0 >> 5) +} + +func (d *ShapeDeserializer) peekMinor() byte { + return d.p[d.off] & 0x1f +} + +func (d *ShapeDeserializer) readArg() (uint64, error) { + if d.eof() { + return 0, errUnexpectedEOF + } + + minor := d.peekMinor() + if minor < minorArg1 { + d.off++ + return uint64(minor), nil + } + + idx := int(minor - minorArg1) + if idx < 0 || idx >= len(argSizes) { + return 0, fmt.Errorf("unexpected minor value %d", minor) + } + + n := argSizes[idx] + if d.off+1+n > len(d.p) { + return 0, errUnexpectedEOF + } + + buf := d.p[d.off+1 : d.off+1+n] + d.off += 1 + n + + switch n { + case 1: + return uint64(buf[0]), nil + case 2: + return uint64(binary.BigEndian.Uint16(buf)), nil + case 4: + return uint64(binary.BigEndian.Uint32(buf)), nil + default: + return binary.BigEndian.Uint64(buf), nil + } +} + +func (d *ShapeDeserializer) readInt64() (int64, error) { + if d.eof() { + return 0, errUnexpectedEOF + } + + major := d.peekMajor() + switch major { + case majorTypeUint: + v, err := d.readArg() + if err != nil { + return 0, err + } + if v > math.MaxInt64 { + return 0, fmt.Errorf("cbor uint %d exceeds max int64", v) + } + return int64(v), nil + case majorTypeNegInt: + v, err := d.readArg() + if err != nil { + return 0, err + } + // CBOR negint: actual value is -1 - v + if v > math.MaxInt64 { + return 0, fmt.Errorf("cbor negint exceeds min int64") + } + return -1 - int64(v), nil + default: + return 0, fmt.Errorf("expected integer, got major type %d", major) + } +} + +func (d *ShapeDeserializer) readFloat64() (float64, error) { + if d.eof() { + return 0, errUnexpectedEOF + } + + major := d.peekMajor() + switch major { + case majorType7: + minor := d.peekMinor() + switch minor { + case major7Float16: + if d.off+3 > len(d.p) { + return 0, errUnexpectedEOF + } + bits := binary.BigEndian.Uint16(d.p[d.off+1 : d.off+3]) + d.off += 3 + return float64(math.Float32frombits(float16to32(bits))), nil + case major7Float32: + if d.off+5 > len(d.p) { + return 0, errUnexpectedEOF + } + bits := binary.BigEndian.Uint32(d.p[d.off+1 : d.off+5]) + d.off += 5 + return float64(math.Float32frombits(bits)), nil + case major7Float64: + if d.off+9 > len(d.p) { + return 0, errUnexpectedEOF + } + bits := binary.BigEndian.Uint64(d.p[d.off+1 : d.off+9]) + d.off += 9 + return math.Float64frombits(bits), nil + default: + return 0, fmt.Errorf("given majorType7, expected a minor of float type, instead got %d", minor) + } + case majorTypeUint: + v, err := d.readArg() + if err != nil { + return 0, err + } + return float64(v), nil + case majorTypeNegInt: + v, err := d.readArg() + if err != nil { + return 0, err + } + return -1 - float64(v), nil + default: + return 0, fmt.Errorf("expected float, got major type %d", major) + } +} + +// ReadNil implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadNil(s *smithy.Schema) (bool, error) { + if d.eof() { + return false, errUnexpectedEOF + } + + if d.peekMajor() == majorType7 { + minor := d.peekMinor() + if minor == major7Nil || minor == major7Undefined { + d.off++ + return true, nil + } + } + return false, nil +} + +// ReadInt8 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt8(s *smithy.Schema, v *int8) error { + return readInt(d, v, math.MinInt8, math.MaxInt8) +} + +// ReadInt16 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt16(s *smithy.Schema, v *int16) error { + return readInt(d, v, math.MinInt16, math.MaxInt16) +} + +// ReadInt32 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt32(s *smithy.Schema, v *int32) error { + return readInt(d, v, math.MinInt32, math.MaxInt32) +} + +// ReadInt64 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { + return readInt(d, v, math.MinInt64, math.MaxInt64) +} + +// ReadInt8Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt8Ptr(s *smithy.Schema, v **int8) error { + return readPtr(d, s, v, d.ReadInt8) +} + +// ReadInt16Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt16Ptr(s *smithy.Schema, v **int16) error { + return readPtr(d, s, v, d.ReadInt16) +} + +// ReadInt32Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt32Ptr(s *smithy.Schema, v **int32) error { + return readPtr(d, s, v, d.ReadInt32) +} + +// ReadInt64Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt64Ptr(s *smithy.Schema, v **int64) error { + return readPtr(d, s, v, d.ReadInt64) +} + +// ReadFloat32 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error { + return readFloat(d, v) +} + +// ReadFloat64 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { + return readFloat(d, v) +} + +// ReadFloat32Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat32Ptr(s *smithy.Schema, v **float32) error { + return readPtr(d, s, v, d.ReadFloat32) +} + +// ReadFloat64Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat64Ptr(s *smithy.Schema, v **float64) error { + return readPtr(d, s, v, d.ReadFloat64) +} + +// ReadBool implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { + if d.eof() { + return errUnexpectedEOF + } + if d.peekMajor() != majorType7 { + return fmt.Errorf("expected bool, got major type %d", d.peekMajor()) + } + minor := d.peekMinor() + switch minor { + case major7True: + *v = true + d.off++ + return nil + case major7False: + *v = false + d.off++ + return nil + default: + return fmt.Errorf("expected bool, got minor %d", minor) + } +} + +// ReadBoolPtr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadBoolPtr(s *smithy.Schema, v **bool) error { + return readPtr(d, s, v, d.ReadBool) +} + +// ReadString implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { + if d.eof() { + return errUnexpectedEOF + } + + if d.peekMajor() != majorTypeString { + return fmt.Errorf("expected string, got major type %d", d.peekMajor()) + } + + if d.peekMinor() == minorIndefinite { + d.off++ + var result []byte + for d.off < len(d.p) && d.p[d.off] != 0xff { + if d.peekMajor() != majorTypeString { + return fmt.Errorf("expected string chunk, got major type %d", d.peekMajor()) + } + slen, err := d.readArg() + if err != nil { + return err + } + if d.off+int(slen) > len(d.p) { + return fmt.Errorf("string chunk length %d exceeds remaining data", slen) + } + result = append(result, d.p[d.off:d.off+int(slen)]...) + d.off += int(slen) + } + d.off++ // skip terminator + *v = string(result) + return nil + } + + slen, err := d.readArg() + if err != nil { + return err + } + if d.off+int(slen) > len(d.p) { + return fmt.Errorf("string length %d exceeds remaining data", slen) + } + *v = string(d.p[d.off : d.off+int(slen)]) + d.off += int(slen) + return nil +} + +// ReadStringPtr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadStringPtr(s *smithy.Schema, v **string) error { + return readPtr(d, s, v, d.ReadString) +} + +// ReadTime implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadTime(schema *smithy.Schema, v *time.Time) error { + if d.eof() { + return errUnexpectedEOF + } + + if d.peekMajor() != majorTypeTag { + return fmt.Errorf("expected tag for timestamp, got major type %d", d.peekMajor()) + } + tagID, err := d.readArg() + if err != nil { + return err + } + if tagID != 1 { + return fmt.Errorf("expected tag 1 for timestamp, got %d", tagID) + } + + if d.eof() { + return errUnexpectedEOF + } + + major := d.peekMajor() + switch major { + case majorTypeUint, majorTypeNegInt: + secs, err := d.readInt64() + if err != nil { + return err + } + *v = time.Unix(secs, 0) + return nil + case majorType7: + f, err := d.readFloat64() + if err != nil { + return err + } + *v = time.UnixMilli(int64(f * 1e3)) + return nil + default: + return fmt.Errorf("unexpected major type %d in timestamp tag", major) + } +} + +// ReadTimePtr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadTimePtr(schema *smithy.Schema, v **time.Time) error { + return readPtr(d, schema, v, d.ReadTime) +} + +// ReadBlob implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { + if isNil, err := d.ReadNil(s); isNil || err != nil { + return err + } + + if d.peekMajor() != majorTypeSlice { + return fmt.Errorf("expected byte string, got major type %d", d.peekMajor()) + } + slen, err := d.readArg() + if err != nil { + return err + } + if d.off+int(slen) > len(d.p) { + return fmt.Errorf("blob length %d exceeds remaining data", slen) + } + *v = make([]byte, slen) + copy(*v, d.p[d.off:d.off+int(slen)]) + d.off += int(slen) + return nil +} + +// ReadList implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { + if d.eof() { + return errUnexpectedEOF + } + + if d.peekMajor() != majorTypeList { + return fmt.Errorf("expected list, got major type %d", d.peekMajor()) + } + if d.peekMinor() == minorIndefinite { + d.off++ + d.head.push(deserCtx{kind: deserCtxList, remaining: -1}) + return nil + } + count, err := d.readArg() + if err != nil { + return err + } + d.head.push(deserCtx{kind: deserCtxList, remaining: int(count)}) + return nil +} + +// ReadListItem implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadListItem(s *smithy.Schema) (bool, error) { + lc := d.head.top() + if lc == nil || lc.kind != deserCtxList { + return false, fmt.Errorf("ReadListItem called without ReadList") + } + + if lc.remaining == -1 { + if d.off < len(d.p) && d.p[d.off] == 0xff { + d.off++ + d.head.pop() + return false, nil + } + return true, nil + } + if lc.remaining <= 0 { + d.head.pop() + return false, nil + } + lc.remaining-- + return true, nil +} + +// ReadMap implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadMap(s *smithy.Schema) error { + if d.eof() { + return errUnexpectedEOF + } + + if d.peekMajor() != majorTypeMap { + return fmt.Errorf("expected map, got major type %d", d.peekMajor()) + } + if d.peekMinor() == minorIndefinite { + d.off++ + d.head.push(deserCtx{kind: deserCtxMap, remaining: -1}) + return nil + } + count, err := d.readArg() + if err != nil { + return err + } + d.head.push(deserCtx{kind: deserCtxMap, remaining: int(count)}) + return nil +} + +// ReadMapKey implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadMapKey(s *smithy.Schema) (string, bool, error) { + mc := d.head.top() + if mc == nil || mc.kind != deserCtxMap { + return "", false, errors.New("ReadMapKey called without ReadMap") + } + + if mc.remaining == -1 { + if d.off < len(d.p) && d.p[d.off] == 0xff { + d.off++ + d.head.pop() + return "", false, nil + } + } else { + if mc.remaining <= 0 { + d.head.pop() + return "", false, nil + } + mc.remaining-- + } + + var key string + if err := d.ReadString(nil, &key); err != nil { + return "", false, err + } + return key, true, nil +} + +// ReadStruct implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error { + if isNil, err := d.ReadNil(s); isNil || err != nil { + return err + } + + if d.peekMajor() != majorTypeMap { + return fmt.Errorf("expected map for struct, got major type %d", d.peekMajor()) + } + if d.peekMinor() == minorIndefinite { + d.off++ + d.head.push(deserCtx{kind: deserCtxStruct, schema: s, remaining: -1}) + return nil + } + count, err := d.readArg() + if err != nil { + return err + } + d.head.push(deserCtx{kind: deserCtxStruct, schema: s, remaining: int(count)}) + return nil +} + +// ReadStructMember implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { + sc := d.head.top() + if sc == nil || sc.kind != deserCtxStruct { + return nil, fmt.Errorf("ReadStructMember called without ReadStruct") + } + + if sc.remaining == -1 { + if d.off < len(d.p) && d.p[d.off] == 0xff { + d.off++ + d.head.pop() + return nil, nil + } + } else { + if sc.remaining <= 0 { + d.head.pop() + return nil, nil + } + sc.remaining-- + } + + var key string + if err := d.ReadString(nil, &key); err != nil { + return nil, err + } + + member := sc.schema.Member(key) + if member == nil { + if err := d.skip(); err != nil { + return nil, err + } + return d.ReadStructMember() + } + + return member, nil +} + +// ReadUnion implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) { + top := d.head.top() + if top == nil || top.kind != deserCtxUnion { // first call: open the map + if d.eof() { + return nil, errUnexpectedEOF + } + if d.peekMajor() != majorTypeMap { + return nil, fmt.Errorf("expected map for union, got major type %d", d.peekMajor()) + } + if d.peekMinor() == minorIndefinite { + d.off++ + d.head.push(deserCtx{kind: deserCtxUnion, schema: s, remaining: -1}) + } else { + count, err := d.readArg() + if err != nil { + return nil, err + } + d.head.push(deserCtx{kind: deserCtxUnion, schema: s, remaining: int(count)}) + } + } + + uc := d.head.top() + for { + if uc.remaining == -1 { + if d.off < len(d.p) && d.p[d.off] == 0xff { + d.off++ + d.head.pop() + return nil, nil + } + } else if uc.remaining <= 0 { + d.head.pop() + return nil, nil + } else { + uc.remaining-- + } + + var key string + if err := d.ReadString(nil, &key); err != nil { + return nil, err + } + + if d.off < len(d.p) && d.peekMajor() == majorType7 { + minor := d.peekMinor() + if minor == major7Nil || minor == major7Undefined { + d.off++ + continue + } + } + + member := s.Member(key) + if member == nil { + if err := d.skip(); err != nil { + return nil, err + } + continue + } + + return member, nil + } +} + +// ReadDocument is unimplemented and will panic. +func (d *ShapeDeserializer) ReadDocument(schema *smithy.Schema, v *document.Value) error { + panic("unimplemented") +} + +func (d *ShapeDeserializer) skip() error { + if d.eof() { + return errUnexpectedEOF + } + major := d.peekMajor() + switch major { + case majorTypeUint, majorTypeNegInt: + _, err := d.readArg() + return err + case majorTypeSlice, majorTypeString: + if d.peekMinor() == minorIndefinite { + return d.skipIndefiniteBytes() + } + slen, err := d.readArg() + if err != nil { + return err + } + d.off += int(slen) + return nil + case majorTypeList, majorTypeMap: + itemsPerEntry := 1 + if major == majorTypeMap { + itemsPerEntry = 2 + } + if d.peekMinor() == minorIndefinite { + d.off++ + for d.off < len(d.p) && d.p[d.off] != 0xff { + for range itemsPerEntry { + if err := d.skip(); err != nil { + return err + } + } + } + d.off++ // skip terminator + return nil + } + count, err := d.readArg() + if err != nil { + return err + } + for range int(count) * itemsPerEntry { + if err := d.skip(); err != nil { + return err + } + } + return nil + case majorTypeTag: + _, err := d.readArg() + if err != nil { + return err + } + return d.skip() // skip the tagged value + case majorType7: + minor := d.peekMinor() + n := 0 + if minor >= minorArg1 && minor <= minorArg8 { + n = major7ExtraSizes[minor-minorArg1] + } + d.off += 1 + n + return nil + default: + return fmt.Errorf("unexpected major type %d", major) + } +} + +func (d *ShapeDeserializer) skipIndefiniteBytes() error { + d.off++ // skip the indefinite marker + for d.off < len(d.p) && d.p[d.off] != 0xff { + if err := d.skip(); err != nil { + return err + } + } + d.off++ // skip break + return nil +} + +func readPtr[T any](d *ShapeDeserializer, s *smithy.Schema, v **T, read func(*smithy.Schema, *T) error) error { + if isNil, err := d.ReadNil(s); isNil || err != nil { + return err + } + if *v == nil { + *v = new(T) + } + return read(s, *v) +} + +type tint interface { + int8 | int16 | int32 | int64 +} + +func readInt[T tint](d *ShapeDeserializer, v *T, min, max int64) error { + n, err := d.readInt64() + if err != nil { + return err + } + if n < min || n > max { + return fmt.Errorf("int %d exceeds %T range", n, *v) + } + *v = T(n) + return nil +} + +type tfloat interface { + float32 | float64 +} + +func readFloat[T tfloat](d *ShapeDeserializer, v *T) error { + n, err := d.readFloat64() + if err != nil { + return err + } + *v = T(n) + return nil +} + +// GetProtocolErrorInfo decodes error type/message from a CBOR response body. +func GetProtocolErrorInfo(p []byte) (typ, message string, err error) { + v, decErr := smithycbor.Decode(p) + if decErr != nil { + return "", "", decErr + } + + m, ok := v.(smithycbor.Map) + if !ok { + return "", "", nil + } + + if t, ok := m["__type"]; ok { + if s, ok := t.(smithycbor.String); ok { + typ = string(s) + } + } + if msg, ok := m["message"]; ok { + if s, ok := msg.(smithycbor.String); ok { + message = string(s) + } + } + + return typ, message, nil +} diff --git a/smithy-http-protocols/internal/cbor/shape_serializer.go b/smithy-http-protocols/internal/cbor/shape_serializer.go new file mode 100644 index 000000000..67d7f1306 --- /dev/null +++ b/smithy-http-protocols/internal/cbor/shape_serializer.go @@ -0,0 +1,456 @@ +package cbor + +import ( + "encoding/binary" + "math" + "math/big" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/traits" +) + +// ShapeSerializer implements marshaling of Smithy shapes to CBOR. +type ShapeSerializer struct { + buf []byte + head []byte + + opts ShapeSerializerOptions + + // rootSchema is the schema passed to the first WriteStruct call, + // used by the protocol to determine if the input is a Unit shape. + rootSchema *smithy.Schema +} + +// ShapeSerializerOptions configures ShapeSerializer. +type ShapeSerializerOptions struct { + WriteZeroValues bool +} + +var _ smithy.ShapeSerializer = (*ShapeSerializer)(nil) + +// NewShapeSerializer creates a new ShapeSerializer. +func NewShapeSerializer(opts ...func(*ShapeSerializerOptions)) *ShapeSerializer { + o := ShapeSerializerOptions{} + for _, fn := range opts { + fn(&o) + } + return &ShapeSerializer{opts: o} +} + +// Bytes returns the serialized CBOR bytes. +func (s *ShapeSerializer) Bytes() []byte { + return s.buf +} + +// IsUnitShape returns true if the serialized content represents a Unit shape +// (a struct with no defined input, marked with the UnitShape trait). +func (s *ShapeSerializer) IsUnitShape() bool { + if s.rootSchema == nil { + return false + } + _, ok := smithy.SchemaTrait[*traits.UnitShape](s.rootSchema) + return ok +} + +func (s *ShapeSerializer) top() byte { + if len(s.head) == 0 { + return 0xff + } + return s.head[len(s.head)-1] +} + +func (s *ShapeSerializer) push(v byte) { + s.head = append(s.head, v) +} + +func (s *ShapeSerializer) pop() { + s.head = s.head[:len(s.head)-1] +} + +// WriteInt8Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8Ptr(schema *smithy.Schema, v *int8) { + if v != nil { + s.withWriteZero(func() { s.WriteInt8(schema, *v) }) + } +} + +// WriteInt16Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16Ptr(schema *smithy.Schema, v *int16) { + if v != nil { + s.withWriteZero(func() { s.WriteInt16(schema, *v) }) + } +} + +// WriteInt32Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32Ptr(schema *smithy.Schema, v *int32) { + if v != nil { + s.withWriteZero(func() { s.WriteInt32(schema, *v) }) + } +} + +// WriteInt64Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64Ptr(schema *smithy.Schema, v *int64) { + if v != nil { + s.withWriteZero(func() { s.WriteInt64(schema, *v) }) + } +} + +// WriteFloat32Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32Ptr(schema *smithy.Schema, v *float32) { + if v != nil { + s.withWriteZero(func() { s.WriteFloat32(schema, *v) }) + } +} + +// WriteFloat64Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64Ptr(schema *smithy.Schema, v *float64) { + if v != nil { + s.withWriteZero(func() { s.WriteFloat64(schema, *v) }) + } +} + +// WriteBoolPtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBoolPtr(schema *smithy.Schema, v *bool) { + if v != nil { + s.withWriteZero(func() { s.WriteBool(schema, *v) }) + } +} + +// WriteStringPtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStringPtr(schema *smithy.Schema, v *string) { + if v != nil { + s.withWriteZero(func() { s.WriteString(schema, *v) }) + } +} + +func (s *ShapeSerializer) withWriteZero(fn func()) { + prev := s.opts.WriteZeroValues + s.opts.WriteZeroValues = true + fn() + s.opts.WriteZeroValues = prev +} + +func (s *ShapeSerializer) skipZeroValue() bool { + if s.opts.WriteZeroValues { + return false + } + if len(s.head) == 0 { + return false + } + switch s.top() { + case ctxList, ctxMapValue: + return false + default: + return true + } +} + +// WriteBool implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { + if !v && s.skipZeroValue() { + return + } + s.writeKey(schema) + if v { + s.buf = append(s.buf, compose(majorType7, major7True)) + } else { + s.buf = append(s.buf, compose(majorType7, major7False)) + } +} + +func (s *ShapeSerializer) writeInt(v int64) { + if v >= 0 { + s.writeUint(uint64(v)) + } else { + s.writeArg(majorTypeNegInt, uint64(-v-1)) + } +} + +func (s *ShapeSerializer) writeUint(v uint64) { + s.writeArg(majorTypeUint, v) +} + +// WriteInt8 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { + if v == 0 && s.skipZeroValue() { + return + } + s.writeKey(schema) + s.writeInt(int64(v)) +} + +// WriteInt16 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { + if v == 0 && s.skipZeroValue() { + return + } + s.writeKey(schema) + s.writeInt(int64(v)) +} + +// WriteInt32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { + if v == 0 && s.skipZeroValue() { + return + } + s.writeKey(schema) + s.writeInt(int64(v)) +} + +// WriteInt64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { + if v == 0 && s.skipZeroValue() { + return + } + s.writeKey(schema) + s.writeInt(v) +} + +// WriteFloat32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { + if v == 0 && !math.Signbit(float64(v)) && s.skipZeroValue() { + return + } + s.writeKey(schema) + s.buf = append(s.buf, compose(majorType7, major7Float32)) + s.buf = binary.BigEndian.AppendUint32(s.buf, math.Float32bits(v)) +} + +// WriteFloat64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { + if v == 0 && !math.Signbit(v) && s.skipZeroValue() { + return + } + s.writeKey(schema) + s.buf = append(s.buf, compose(majorType7, major7Float64)) + s.buf = binary.BigEndian.AppendUint64(s.buf, math.Float64bits(v)) +} + +// WriteString implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { + if v == "" && s.skipZeroValue() { + return + } + s.writeKey(schema) + s.writeTextString(v) +} + +// WriteBlob implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { + if v == nil { + return + } + s.writeKey(schema) + s.writeArg(majorTypeSlice, uint64(len(v))) + s.buf = append(s.buf, v...) +} + +// WriteList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { + s.writeKey(schema) + if s.top() == ctxMapValue { + s.pop() + } + s.buf = append(s.buf, compose(majorTypeList, minorIndefinite)) + s.push(ctxList) +} + +// CloseList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseList() { + if s.top() != ctxList { + return + } + s.pop() + s.buf = append(s.buf, 0xff) +} + +// WriteMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { + if s.rootSchema == nil && len(s.head) == 0 { + s.rootSchema = schema + } + s.writeKey(schema) + if s.top() == ctxMapValue { + s.pop() + } + s.buf = append(s.buf, compose(majorTypeMap, minorIndefinite)) + s.push(ctxMap) +} + +// WriteKey implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteKey(_ *smithy.Schema, key string) { + s.writeTextString(key) + s.push(ctxMapValue) +} + +// CloseMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseMap() { + if s.top() != ctxMap { + return + } + s.pop() + s.buf = append(s.buf, 0xff) +} + +// WriteTime implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { + s.writeKey(schema) + if s.top() == ctxMapValue { + s.pop() + } + + // rpcv2Cbor: always epoch-seconds as float64, tag 1 + epoch := float64(v.UnixMilli()) / 1e3 + s.writeArg(majorTypeTag, 1) + s.buf = append(s.buf, compose(majorType7, major7Float64)) + s.buf = binary.BigEndian.AppendUint64(s.buf, math.Float64bits(epoch)) +} + +// WriteTimePtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { + if v != nil { + s.WriteTime(schema, *v) + } +} + +// WriteUnion implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema, v smithy.Serializable) { + s.writeKey(schema) + // union is a map with a single key + s.writeArg(majorTypeMap, 1) + s.writeTextString(variant.MemberName()) + v.Serialize(s) +} + +// WriteStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema, v smithy.Serializable) { + if v == nil { + return + } + s.writeKey(schema) + if s.top() == ctxMapValue { + s.pop() + } + v.Serialize(s) +} + +// WriteNil implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteNil(schema *smithy.Schema) { + s.writeKey(schema) + if s.top() == ctxMapValue { + s.pop() + } + s.buf = append(s.buf, compose(majorType7, major7Nil)) +} + +// WriteBigInteger is unimplemented and will panic. +func (s *ShapeSerializer) WriteBigInteger(schema *smithy.Schema, v big.Int) { + panic("unimplemented") +} + +// WriteBigDecimal is unimplemented and will panic. +func (s *ShapeSerializer) WriteBigDecimal(schema *smithy.Schema, v big.Float) { + panic("unimplemented") +} + +// WriteDocument is unimplemented and will panic. +func (s *ShapeSerializer) WriteDocument(schema *smithy.Schema, v document.Value) { + panic("unimplemented") +} + +// writeKey writes the member name as a CBOR text string key when inside a +// struct or map context. +func (s *ShapeSerializer) writeKey(schema *smithy.Schema) { + // If we're in a map value context (after WriteKey), just pop it and + // don't write a key - the key was already written by WriteKey. + if s.top() == ctxMapValue { + s.pop() + return + } + if schema == nil { + return + } + + if s.top() == ctxMap { + name := schema.MemberName() + if name != "" { + s.writeTextString(name) + } + } +} + +func (s *ShapeSerializer) writeTextString(v string) { + s.writeArg(majorTypeString, uint64(len(v))) + s.buf = append(s.buf, v...) +} + +func (s *ShapeSerializer) writeArg(major majorType, arg uint64) { + if arg < 24 { + s.buf = append(s.buf, byte(major)<<5|byte(arg)) + } else if arg < 0x100 { + s.buf = append(s.buf, compose(major, minorArg1), byte(arg)) + } else if arg < 0x10000 { + s.buf = append(s.buf, compose(major, minorArg2)) + s.buf = binary.BigEndian.AppendUint16(s.buf, uint16(arg)) + } else if arg < 0x100000000 { + s.buf = append(s.buf, compose(major, minorArg4)) + s.buf = binary.BigEndian.AppendUint32(s.buf, uint32(arg)) + } else { + s.buf = append(s.buf, compose(major, minorArg8)) + s.buf = binary.BigEndian.AppendUint64(s.buf, arg) + } +} + +// duplicated from the old encoding/cbor + +type majorType byte + +const ( + majorTypeUint majorType = 0 + majorTypeNegInt majorType = 1 + majorTypeSlice majorType = 2 + majorTypeString majorType = 3 + majorTypeList majorType = 4 + majorTypeMap majorType = 5 + majorTypeTag majorType = 6 + majorType7 majorType = 7 +) + +const ( + minorArg1 = 24 + minorArg2 = 25 + minorArg4 = 26 + minorArg8 = 27 + minorIndefinite = 31 +) + +const ( + major7False = 20 + major7True = 21 + major7Nil = 22 + major7Undefined = 23 + major7Float16 = minorArg2 + major7Float32 = minorArg4 + major7Float64 = minorArg8 +) + +// maps minor argument indicators (minorArg1..minorArg8) to the number of bytes +// that follow for the argument value +var argSizes = [4]int{1, 2, 4, 8} + +// maps minor values (minorArg1..minorArg8) in major type 7 to the number of +// payload bytes that follow +var major7ExtraSizes = [4]int{0, 2, 4, 8} + +func compose(major majorType, minor byte) byte { + return byte(major)<<5 | minor +} + +// context sentinels for the serialization state stack +const ( + ctxList byte = iota // inside a list + ctxMap // inside a map (struct or smithy map) + ctxMapValue // next write is a map value (after WriteKey) +) diff --git a/smithy-http-protocols/rpcv2/rpcv2.go b/smithy-http-protocols/rpcv2/rpcv2.go new file mode 100644 index 000000000..ca9f68b64 --- /dev/null +++ b/smithy-http-protocols/rpcv2/rpcv2.go @@ -0,0 +1,232 @@ +package rpcv2 + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/eventstream" + internalerrors "github.com/aws/smithy-go/internal/errors" + smithyio "github.com/aws/smithy-go/io" + "github.com/aws/smithy-go/middleware" + internalcbor "github.com/aws/smithy-go/smithy-http-protocols/internal/cbor" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Protocol implements an RPC v2 protocol. +// +// RPCv2 protocol family: +// - CBOR: https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html +type Protocol struct { + options ProtocolOptions + + codec smithy.Codec + contentType string + protocolID string + + *eventstream.Codec +} + +// ProtocolOptions configures a Protocol. +type ProtocolOptions struct { + // When enabled, support reading legacy AWS query error codes in error + // responses. + // + // See https://smithy.io/2.0/aws/protocols/aws-query-protocol.html#aws-protocols-awsquerycompatible-trait. + UseQueryCompatible bool +} + +// UseQueryCompatible enables support for AWS query compatibility. +func UseQueryCompatible(o *ProtocolOptions) { + o.UseQueryCompatible = true +} + +var _ smithyhttp.ClientProtocol = (*Protocol)(nil) + +// NewCBOR returns an instance of the smithy.protocols#rpcv2Cbor protocol. +func NewCBOR(opts ...func(*ProtocolOptions)) *Protocol { + var options ProtocolOptions + for _, opt := range opts { + opt(&options) + } + codec := &internalcbor.Codec{} + return &Protocol{ + options: options, + codec: codec, + contentType: "application/cbor", + protocolID: "smithy.protocols#rpcv2Cbor", + Codec: &eventstream.Codec{ + Codec: codec, + ContentType: "application/cbor", + }, + } +} + +// ID identifies the protocol. +func (p *Protocol) ID() string { + return p.protocolID +} + +// SerializeRequest serializes a request for rpcv2Cbor. +func (p *Protocol) SerializeRequest( + ctx context.Context, + schema *smithy.OperationSchema, + in smithy.Serializable, + req *smithyhttp.Request, +) error { + req.Method = http.MethodPost + req.URL.Path = fmt.Sprintf("/service/%s/operation/%s", + middleware.GetServiceName(ctx), middleware.GetOperationName(ctx)) + req.Header.Set("Smithy-Protocol", "rpc-v2-cbor") + req.Header.Set("Accept", "application/cbor") + if p.options.UseQueryCompatible { + req.Header.Set("X-Amzn-Query-Mode", "true") + } + + if schema.IsInputEventStream() { + req.Header.Set("Content-Type", "application/vnd.amazon.eventstream") + return nil + } + + ss := p.codec.Serializer() + in.Serialize(ss) + + payload := ss.Bytes() + if len(payload) == 0 { + return nil + } + + // operations targeting Unit MUST NOT have a body, check if we backfilled + // an input + if cs, ok := ss.(*internalcbor.ShapeSerializer); ok && cs.IsUnitShape() { + return nil + } + + req.Header.Set("Content-Type", p.contentType) + + sreq, err := req.SetStream(bytes.NewReader(payload)) + if err != nil { + return fmt.Errorf("set stream: %w", err) + } + + *req = *sreq + return nil +} + +// DeserializeResponse deserializes a response for rpcv2Cbor. +func (p *Protocol) DeserializeResponse( + ctx context.Context, + schema *smithy.OperationSchema, + types *smithy.TypeRegistry, + resp *smithyhttp.Response, + out smithy.Deserializable, +) error { + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return p.deserializeError(types, resp) + } + + if schema.IsOutputEventStream() { + return nil + } + + payload, err := io.ReadAll(resp.Body) + if err != nil { + return &smithy.DeserializationError{Err: err} + } + + if len(payload) == 0 { + return nil + } + + sd := p.codec.Deserializer(payload) + if err := out.Deserialize(sd); err != nil { + return &smithy.DeserializationError{Err: err} + } + + return nil +} + +// HasInitialEventMessage is true because this is an RPC protocol. +func (*Protocol) HasInitialEventMessage() bool { + return true +} + +func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithyhttp.Response) error { + var errorBuffer bytes.Buffer + if _, err := io.Copy(&errorBuffer, response.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("failed to copy error response body, %w", err)} + } + errorBody := errorBuffer.Bytes() + + errorCode := "UnknownError" + errorMessage := errorCode + + var buff [1024]byte + ringBuffer := smithyio.NewRingBuffer(buff[:]) + + body := io.TeeReader(bytes.NewReader(errorBody), ringBuffer) + bodyBytes, err := io.ReadAll(body) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to read response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + bodyType, bodyMessage, err := internalcbor.GetProtocolErrorInfo(bodyBytes) + if err != nil { + var snapshot bytes.Buffer + io.Copy(&snapshot, ringBuffer) + return &smithy.DeserializationError{ + Err: fmt.Errorf("failed to decode response body, %w", err), + Snapshot: snapshot.Bytes(), + } + } + + if bodyType != "" { + errorCode = bodyType + } + if bodyMessage != "" { + errorMessage = bodyMessage + } + + errorCode = internalerrors.SanitizeErrorCode(errorCode) + + var queryCode string + var queryFault smithy.ErrorFault + if p.options.UseQueryCompatible { + queryHeader := response.Header.Get("X-Amzn-Query-Error") + queryCode, queryFault = internalerrors.ParseQueryError(queryHeader) + } + + perr, ok := types.DeserializableError(errorCode) + if !ok { + code := errorCode + if queryCode != "" { + code = queryCode + } + return &smithy.GenericAPIError{ + Code: code, + Message: errorMessage, + Fault: queryFault, + } + } + + if len(bodyBytes) > 0 { + deser := p.codec.Deserializer(bodyBytes) + if err := perr.Deserialize(deser); err != nil { + return &smithy.DeserializationError{Err: err} + } + } + + if queryCode != "" { + internalerrors.SetErrorCodeOverride(perr, queryCode) + } + + return perr +} diff --git a/traits/traits.go b/traits/traits.go index 9d9cb9b83..55d26f58c 100644 --- a/traits/traits.go +++ b/traits/traits.go @@ -46,3 +46,11 @@ type AWSQueryError struct { // TraitID identifies the trait. func (*AWSQueryError) TraitID() string { return "aws.protocols#awsQueryError" } + +// UnitShape is a synthetic trait applied to input/output shapes that were +// backfilled from Unit. It indicates the shape has no defined members and +// should be treated as absent for protocol serialization purposes. +type UnitShape struct{} + +// TraitID identifies the trait. +func (*UnitShape) TraitID() string { return "smithy.go#unitShape" } From 5f4544d8d145d9ca35d831cf17853ae1247b902e Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Mon, 27 Apr 2026 13:12:32 -0400 Subject: [PATCH 09/38] implement awsquery/ec2query (#653) * implement awsquery/ec2query * feedback1 * Update aws-protocols/internal/xml/error.go Co-authored-by: Luis Madrigal <599908+Madrigal@users.noreply.github.com> --------- Co-authored-by: Luis Madrigal <599908+Madrigal@users.noreply.github.com> --- aws-protocols/awsquery/awsquery.go | 150 ++++ aws-protocols/ec2query/ec2query.go | 125 ++++ .../internal/query/shape_deserializer.go | 4 + .../internal/query/shape_serializer.go | 615 ++++++++++++++++ aws-protocols/internal/xml/error.go | 41 ++ aws-protocols/internal/xml/extract.go | 72 ++ .../internal/xml/shape_deserializer.go | 686 ++++++++++++++++++ .../go/codegen/DefaultTraitGenerators.java | 3 + .../smithy/go/codegen/ServiceGenerator.java | 13 + .../go/codegen/SimpleTraitGenerator.java | 7 + .../smithy/go/codegen/SmithyGoDependency.java | 2 + eventstream/no_event_stream.go | 36 + serde.go | 5 + trait.go | 4 + traits/traits.go | 8 + 15 files changed, 1771 insertions(+) create mode 100644 aws-protocols/awsquery/awsquery.go create mode 100644 aws-protocols/ec2query/ec2query.go create mode 100644 aws-protocols/internal/query/shape_deserializer.go create mode 100644 aws-protocols/internal/query/shape_serializer.go create mode 100644 aws-protocols/internal/xml/error.go create mode 100644 aws-protocols/internal/xml/extract.go create mode 100644 aws-protocols/internal/xml/shape_deserializer.go create mode 100644 eventstream/no_event_stream.go diff --git a/aws-protocols/awsquery/awsquery.go b/aws-protocols/awsquery/awsquery.go new file mode 100644 index 000000000..7498f7d72 --- /dev/null +++ b/aws-protocols/awsquery/awsquery.go @@ -0,0 +1,150 @@ +package awsquery + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http" + + "github.com/aws/smithy-go" + internalquery "github.com/aws/smithy-go/aws-protocols/internal/query" + internalxml "github.com/aws/smithy-go/aws-protocols/internal/xml" + "github.com/aws/smithy-go/eventstream" + "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/traits" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Protocol implements aws.protocols#awsQuery. +type Protocol struct { + eventstream.NoEventStream + + // Service API version (e.g. "2020-01-08"), sent as the "Version" parameter + // in every request. + Version string + + // the query protocols do not have a "codec", they just inline a query + // serializer and xml deserializer +} + +var _ smithyhttp.ClientProtocol = (*Protocol)(nil) + +// New returns an instance of the awsQuery protocol. +func New(version string) *Protocol { + return &Protocol{Version: version} +} + +// ID identifies the protocol. +func (*Protocol) ID() string { + return "aws.protocols#awsQuery" +} + +// SerializeRequest serializes a request for awsQuery. +func (p *Protocol) SerializeRequest( + ctx context.Context, + schema *smithy.OperationSchema, + in smithy.Serializable, + req *smithyhttp.Request, +) error { + req.Method = http.MethodPost + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + ss := internalquery.NewShapeSerializer(middleware.GetOperationName(ctx), p.Version) + in.Serialize(ss) + + sreq, err := req.SetStream(bytes.NewReader(ss.Bytes())) + if err != nil { + return fmt.Errorf("set stream: %w", err) + } + + *req = *sreq + return nil +} + +// DeserializeResponse deserializes a response for awsQuery. +func (p *Protocol) DeserializeResponse( + ctx context.Context, + schema *smithy.OperationSchema, + types *smithy.TypeRegistry, + resp *smithyhttp.Response, + out smithy.Deserializable, +) error { + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return p.deserializeError(types, resp) + } + + payload, err := io.ReadAll(resp.Body) + if err != nil { + return &smithy.DeserializationError{Err: err} + } + + if len(payload) == 0 { + return nil + } + + inner, err := internalxml.ExtractElement(payload, middleware.GetOperationName(ctx)+"Result") + if err != nil { + return &smithy.DeserializationError{Err: err} + } + + if len(inner) == 0 { + return nil + } + + sd := internalxml.NewShapeDeserializer(inner) + return out.Deserialize(sd) +} + +func (p *Protocol) deserializeError(types *smithy.TypeRegistry, resp *smithyhttp.Response) error { + payload, err := io.ReadAll(resp.Body) + if err != nil { + return &smithy.DeserializationError{Err: err} + } + + errorCode, errorMessage, errorBody, err := internalxml.GetProtocolErrorInfo(payload) + if err != nil { + return &smithy.DeserializationError{Err: err} + } + + // resolveError checks both direct shape name and @awsQueryError trait. + perr, ok := resolveError(types, errorCode) + if !ok { + return &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + } + + if len(errorBody) > 0 { + sd := internalxml.NewShapeDeserializer(errorBody) + if err := perr.Deserialize(sd); err != nil { + return &smithy.DeserializationError{Err: err} + } + } + + return perr +} + +func resolveError(types *smithy.TypeRegistry, code string) (smithy.DeserializableError, bool) { + if perr, ok := types.DeserializableError(code); ok { + return perr, true + } + + for _, entry := range types.Entries { + if entry.Schema == nil { + continue + } + + if t, ok := smithy.SchemaTrait[*traits.AWSQueryError](entry.Schema); ok { + if t.ErrorCode == code { + v := entry.New() + if perr, ok := v.(smithy.DeserializableError); ok { + return perr, true + } + } + } + } + + return nil, false +} diff --git a/aws-protocols/ec2query/ec2query.go b/aws-protocols/ec2query/ec2query.go new file mode 100644 index 000000000..273d1c093 --- /dev/null +++ b/aws-protocols/ec2query/ec2query.go @@ -0,0 +1,125 @@ +package ec2query + +import ( + "context" + "fmt" + "io" + "net/http" + "bytes" + + "github.com/aws/smithy-go" + internalquery "github.com/aws/smithy-go/aws-protocols/internal/query" + internalxml "github.com/aws/smithy-go/aws-protocols/internal/xml" + "github.com/aws/smithy-go/eventstream" + "github.com/aws/smithy-go/middleware" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Protocol implements aws.protocols#ec2Query. +type Protocol struct { + eventstream.NoEventStream + + // Service API version (e.g. "2016-11-15"), sent as the "Version" + // parameter in every request. + Version string +} + +var _ smithyhttp.ClientProtocol = (*Protocol)(nil) + +// New returns an instance of the ec2Query protocol. +func New(version string) *Protocol { + return &Protocol{Version: version} +} + +// ID identifies the protocol. +func (*Protocol) ID() string { + return "aws.protocols#ec2Query" +} + +// SerializeRequest serializes a request for ec2Query. +func (p *Protocol) SerializeRequest( + ctx context.Context, + schema *smithy.OperationSchema, + in smithy.Serializable, + req *smithyhttp.Request, +) error { + req.Method = http.MethodPost + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + ss := internalquery.NewShapeSerializer(middleware.GetOperationName(ctx), p.Version, + func(o *internalquery.ShapeSerializerOptions) { o.EC2Mode = true }, + ) + in.Serialize(ss) + + sreq, err := req.SetStream(bytes.NewReader(ss.Bytes())) + if err != nil { + return fmt.Errorf("set stream: %w", err) + } + + *req = *sreq + return nil +} + +// DeserializeResponse deserializes a response for ec2Query. +func (p *Protocol) DeserializeResponse( + ctx context.Context, + schema *smithy.OperationSchema, + types *smithy.TypeRegistry, + resp *smithyhttp.Response, + out smithy.Deserializable, +) error { + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return p.deserializeError(types, resp) + } + + payload, err := io.ReadAll(resp.Body) + if err != nil { + return &smithy.DeserializationError{Err: err} + } + + if len(payload) == 0 { + return nil + } + + inner, err := internalxml.ExtractElement(payload, middleware.GetOperationName(ctx)+"Response") + if err != nil { + return &smithy.DeserializationError{Err: err} + } + + if len(inner) == 0 { + return nil + } + + sd := internalxml.NewShapeDeserializer(inner) + return out.Deserialize(sd) +} + +func (p *Protocol) deserializeError(types *smithy.TypeRegistry, resp *smithyhttp.Response) error { + payload, err := io.ReadAll(resp.Body) + if err != nil { + return &smithy.DeserializationError{Err: err} + } + + errorCode, errorMessage, errorBody, err := internalxml.GetProtocolErrorInfo(payload) + if err != nil { + return &smithy.DeserializationError{Err: err} + } + + // ec2query does not support @awsQueryError so this is a straight lookup + perr, ok := types.DeserializableError(errorCode) + if !ok { + return &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + } + + if len(errorBody) > 0 { + sd := internalxml.NewShapeDeserializer(errorBody) + if err := perr.Deserialize(sd); err != nil { + return &smithy.DeserializationError{Err: err} + } + } + + return perr +} diff --git a/aws-protocols/internal/query/shape_deserializer.go b/aws-protocols/internal/query/shape_deserializer.go new file mode 100644 index 000000000..179ab3d4c --- /dev/null +++ b/aws-protocols/internal/query/shape_deserializer.go @@ -0,0 +1,4 @@ +package query + +// ShapeDeserializer doesn't exist for query. The awsquery and ec2query +// protocols use XML responses. diff --git a/aws-protocols/internal/query/shape_serializer.go b/aws-protocols/internal/query/shape_serializer.go new file mode 100644 index 000000000..635996945 --- /dev/null +++ b/aws-protocols/internal/query/shape_serializer.go @@ -0,0 +1,615 @@ +package query + +import ( + "encoding/base64" + "math" + "math/big" + "net/url" + "sort" + "strconv" + "strings" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + smithytime "github.com/aws/smithy-go/time" + "github.com/aws/smithy-go/traits" +) + +type ctxKind byte + +const ( + ctxKindList ctxKind = iota + ctxKindMap + ctxKindMapValue + ctxKindStruct + + ctxKindNone ctxKind = 0xff +) + +type serCtx struct { + kind ctxKind + flattened bool + prefix string // popped back onto s.currPrefix as you pop out the stack + + listIndex int + listPrefix string + + mapIndex int + mapKeyName string + mapValueName string + mapBuf []mapBufEntry +} + +// ShapeSerializer serializes Smithy shapes to the AWS query string format. +type ShapeSerializer struct { + opts ShapeSerializerOptions + + values url.Values + stack []serCtx + currPrefix string // runs as values are written e.g. for list +} + +type mapBufEntry struct { + prefix string + key string + values []kv +} + +type kv struct { + k, v string +} + +// ShapeSerializerOptions configures a ShapeSerializer. +type ShapeSerializerOptions struct { + WriteZeroValues bool + + // Adjusts serialization for the ec2Query protocol: + // - Member names resolve via @ec2QueryName, then @xmlName + // capitalized, then member name capitalized (instead of @xmlName + // or member name as-is). + // - All lists serialize as flat regardless of @xmlFlattened. + // - Empty lists are omitted (instead of emitting a "Key=" sentinel). + EC2Mode bool +} + +var _ smithy.ShapeSerializer = (*ShapeSerializer)(nil) + +// NewShapeSerializer returns a new ShapeSerializer. +func NewShapeSerializer(action, version string, opts ...func(*ShapeSerializerOptions)) *ShapeSerializer { + o := ShapeSerializerOptions{} + for _, fn := range opts { + fn(&o) + } + v := url.Values{} + v.Set("Action", action) + v.Set("Version", version) + return &ShapeSerializer{values: v, opts: o} +} + +// Bytes returns the encoded query string as bytes. +func (s *ShapeSerializer) Bytes() []byte { + keys := make([]string, 0, len(s.values)) + for k := range s.values { + keys = append(keys, k) + } + sort.Strings(keys) + + var buf strings.Builder + for _, k := range keys { + for _, v := range s.values[k] { + if buf.Len() > 0 { + buf.WriteByte('&') + } + buf.WriteString(url.QueryEscape(k)) + buf.WriteByte('=') + buf.WriteString(url.QueryEscape(v)) + } + } + return []byte(buf.String()) +} + +func (s *ShapeSerializer) top() ctxKind { + if len(s.stack) == 0 { + return ctxKindNone + } + return s.stack[len(s.stack)-1].kind +} + +func (s *ShapeSerializer) push(ctx serCtx) { + s.stack = append(s.stack, ctx) +} + +func (s *ShapeSerializer) pop() serCtx { + n := len(s.stack) + v := s.stack[n-1] + s.stack = s.stack[:n-1] + return v +} + +func (s *ShapeSerializer) topCtx() *serCtx { + return &s.stack[len(s.stack)-1] +} + +func (s *ShapeSerializer) withWriteZero(fn func()) { + prev := s.opts.WriteZeroValues + s.opts.WriteZeroValues = true + fn() + s.opts.WriteZeroValues = prev +} + +func (s *ShapeSerializer) skipZeroValue() bool { + if s.opts.WriteZeroValues { + return false + } + if len(s.stack) == 0 { + return false + } + + switch s.top() { + case ctxKindList, ctxKindMapValue: + return false + default: + return true + } +} + +func (s *ShapeSerializer) memberName(schema *smithy.Schema) string { + if s.opts.EC2Mode { + return ec2MemberName(schema) + } + if xn, ok := smithy.SchemaTrait[*traits.XMLName](schema); ok { + return xn.Name + } + return schema.MemberName() +} + +func (s *ShapeSerializer) appendMemberPrefix(schema *smithy.Schema) { + name := s.memberName(schema) + if name == "" { + return + } + if s.currPrefix != "" { + s.currPrefix = s.currPrefix + "." + name + } else { + s.currPrefix = name + } +} + +// nexus point (alongside writeValue) through which basically every write flows +// to handle putting the appropriate prefix in place +func (s *ShapeSerializer) resolveKey(schema *smithy.Schema) string { + switch s.top() { + case ctxKindMapValue: + valPrefix := s.consumeMapValue() + return valPrefix + case ctxKindList: + ctx := s.topCtx() + ctx.listIndex++ + return s.currPrefix + "." + strconv.Itoa(ctx.listIndex) + default: + name := s.memberName(schema) + if name == "" { + return s.currPrefix + } + if s.currPrefix == "" { + return name + } + return s.currPrefix + "." + name + } +} + +func (s *ShapeSerializer) writeValue(key, value string) { + if s.bufferMapEntry(key, value) { + return + } + + s.values.Add(key, value) +} + +func (s *ShapeSerializer) bufferMapEntry(key, value string) bool { + for i := len(s.stack) - 1; i >= 0; i-- { + ctx := &s.stack[i] + if ctx.kind != ctxKindMap { + continue + } + if len(ctx.mapBuf) == 0 { + return false + } + + entry := &ctx.mapBuf[len(ctx.mapBuf)-1] + suffix := strings.TrimPrefix(key, entry.prefix) + entry.values = append(entry.values, kv{k: suffix, v: value}) + return true + } + + return false +} + +// WriteInt8Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8Ptr(schema *smithy.Schema, v *int8) { + writePtr(s, schema, v, (*ShapeSerializer).WriteInt8) +} + +// WriteInt16Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16Ptr(schema *smithy.Schema, v *int16) { + writePtr(s, schema, v, (*ShapeSerializer).WriteInt16) +} + +// WriteInt32Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32Ptr(schema *smithy.Schema, v *int32) { + writePtr(s, schema, v, (*ShapeSerializer).WriteInt32) +} + +// WriteInt64Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64Ptr(schema *smithy.Schema, v *int64) { + writePtr(s, schema, v, (*ShapeSerializer).WriteInt64) +} + +// WriteFloat32Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32Ptr(schema *smithy.Schema, v *float32) { + writePtr(s, schema, v, (*ShapeSerializer).WriteFloat32) +} + +// WriteFloat64Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64Ptr(schema *smithy.Schema, v *float64) { + writePtr(s, schema, v, (*ShapeSerializer).WriteFloat64) +} + +// WriteBoolPtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBoolPtr(schema *smithy.Schema, v *bool) { + writePtr(s, schema, v, (*ShapeSerializer).WriteBool) +} + +// WriteStringPtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStringPtr(schema *smithy.Schema, v *string) { + writePtr(s, schema, v, (*ShapeSerializer).WriteString) +} + +func writePtr[T any](s *ShapeSerializer, schema *smithy.Schema, v *T, write func(*ShapeSerializer, *smithy.Schema, T)) { + if v == nil { + return + } + + s.withWriteZero(func() { write(s, schema, *v) }) +} + +// WriteBool implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { + if !v && s.skipZeroValue() { + return + } + + if v { + s.writeValue(s.resolveKey(schema), "true") + } else { + s.writeValue(s.resolveKey(schema), "false") + } +} + +// WriteInt8 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { writeInt(s, schema, v) } + +// WriteInt16 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { writeInt(s, schema, v) } + +// WriteInt32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { writeInt(s, schema, v) } + +// WriteInt64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { writeInt(s, schema, v) } + +// WriteFloat32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { writeFloat(s, schema, v) } + +// WriteFloat64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { writeFloat(s, schema, v) } + +func writeInt[T int8 | int16 | int32 | int64](s *ShapeSerializer, schema *smithy.Schema, v T) { + if v == 0 && s.skipZeroValue() { + return + } + + s.writeValue(s.resolveKey(schema), strconv.FormatInt(int64(v), 10)) +} + +func writeFloat[T float32 | float64](s *ShapeSerializer, schema *smithy.Schema, v T) { + if v == 0 && s.skipZeroValue() { + return + } + + s.writeValue(s.resolveKey(schema), formatFloat(float64(v))) +} + +func formatFloat(v float64) string { + switch { + case math.IsInf(v, 1): + return "Infinity" + case math.IsInf(v, -1): + return "-Infinity" + case math.IsNaN(v): + return "NaN" + default: + return strconv.FormatFloat(v, 'f', -1, 64) + } +} + +// WriteString implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { + if v == "" && s.skipZeroValue() { + return + } + + s.writeValue(s.resolveKey(schema), v) +} + +// WriteBlob implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { + if v == nil { + return + } + + s.writeValue(s.resolveKey(schema), base64.StdEncoding.EncodeToString(v)) +} + +// WriteTime implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { + format := "date-time" + if t, ok := smithy.SchemaTrait[*traits.TimestampFormat](schema); ok { + format = t.Format + } + + var sv string + switch format { + case "date-time": + sv = smithytime.FormatDateTime(v) + case "http-date": + sv = smithytime.FormatHTTPDate(v) + case "epoch-seconds": + sv = strconv.FormatFloat(smithytime.FormatEpochSeconds(v), 'f', -1, 64) + default: + sv = smithytime.FormatDateTime(v) + } + + s.writeValue(s.resolveKey(schema), sv) +} + +// WriteTimePtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { + if v != nil { + s.WriteTime(schema, *v) + } +} + +// WriteStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema, v smithy.Serializable) { + if v == nil { + return + } + + saved := s.currPrefix + switch s.top() { + case ctxKindMapValue: + valPrefix := s.consumeMapValue() + s.currPrefix, saved = valPrefix, s.currPrefix + case ctxKindList: + ctx := s.topCtx() + ctx.listIndex++ + s.currPrefix = s.currPrefix + "." + strconv.Itoa(ctx.listIndex) + default: + s.appendMemberPrefix(schema) + } + + v.Serialize(s) + s.currPrefix = saved +} + +// WriteUnion implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema, v smithy.Serializable) { + saved := s.currPrefix + s.appendMemberPrefix(schema) + if s.top() == ctxKindMapValue { + s.pop() + } + v.Serialize(s) + s.currPrefix = saved +} + +// WriteNil implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteNil(_ *smithy.Schema) { + if s.top() == ctxKindMapValue { + s.consumeMapValue() + } +} + +// enterContainer handles the prefix setup common to WriteList and WriteMap. +// It returns the prefix to save for restoration on close. +func (s *ShapeSerializer) enterContainer(schema *smithy.Schema) string { + saved := s.currPrefix + switch s.top() { + case ctxKindMapValue: + // WriteKey set s.prefix to the value path and pushed ctxMapValue + // with the map prefix. Pop ctxMapValue and use its saved prefix + // as the restore point — s.prefix (the value path) is already + // the correct working prefix. + ctx := s.pop() + saved = ctx.prefix + return saved + case ctxKindList: + ctx := s.topCtx() + ctx.listIndex++ + s.currPrefix = s.currPrefix + "." + strconv.Itoa(ctx.listIndex) + } + + s.appendMemberPrefix(schema) + return saved +} + +// WriteList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { + saved := s.enterContainer(schema) + + _, flattened := smithy.SchemaTrait[*traits.XMLFlattened](schema) + flattened = flattened || s.opts.EC2Mode + + listPrefix := s.currPrefix + if !flattened { + locName := "member" + if schema != nil { + if lm := schema.ListMember(); lm != nil { + if xn, ok := smithy.SchemaTrait[*traits.XMLName](lm); ok { + locName = xn.Name + } + } + } + s.currPrefix = s.currPrefix + "." + locName + } + + s.push(serCtx{ + kind: ctxKindList, + flattened: flattened, + prefix: saved, + listPrefix: listPrefix, + }) +} + +// CloseList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseList() { + if s.top() != ctxKindList { + return + } + + ctx := s.pop() + if ctx.listIndex == 0 && !s.opts.EC2Mode { + s.writeValue(ctx.listPrefix, "") + } + + s.currPrefix = ctx.prefix +} + +// WriteMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { + // Structs use WriteMap/CloseMap as a generic "begin object" scope. + if schema != nil && schema.Type() == smithy.ShapeTypeStructure { + saved := s.enterContainer(schema) + s.push(serCtx{kind: ctxKindStruct, prefix: saved}) + return + } + + saved := s.enterContainer(schema) + + _, flattened := smithy.SchemaTrait[*traits.XMLFlattened](schema) + if !flattened { + s.currPrefix = s.currPrefix + ".entry" + } + + keyLoc, valLoc := "key", "value" + if schema != nil { + if mk := schema.MapKey(); mk != nil { + if xn, ok := smithy.SchemaTrait[*traits.XMLName](mk); ok { + keyLoc = xn.Name + } + } + if mv := schema.MapValue(); mv != nil { + if xn, ok := smithy.SchemaTrait[*traits.XMLName](mv); ok { + valLoc = xn.Name + } + } + } + + s.push(serCtx{ + kind: ctxKindMap, + flattened: flattened, + prefix: saved, + mapKeyName: keyLoc, + mapValueName: valLoc, + }) +} + +// WriteKey implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteKey(_ *smithy.Schema, key string) { + ctx := s.topCtx() + ctx.mapIndex++ + + prefix := s.currPrefix + "." + strconv.Itoa(ctx.mapIndex) + ctx.mapBuf = append(ctx.mapBuf, mapBufEntry{ + prefix: prefix, + key: key, + }) + + s.writeValue(prefix+"."+ctx.mapKeyName, key) + + // Push ctxMapValue with the current prefix so the next value write can + // restore it. Set s.prefix to the value path. + s.push(serCtx{kind: ctxKindMapValue, prefix: s.currPrefix}) + s.currPrefix = prefix + "." + ctx.mapValueName +} + +// CloseMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseMap() { + if s.top() == ctxKindStruct { + ctx := s.pop() + s.currPrefix = ctx.prefix + return + } + if s.top() != ctxKindMap { + return + } + + ctx := s.pop() + + // Sort entries by map key for deterministic output. + sort.Slice(ctx.mapBuf, func(i, j int) bool { + return ctx.mapBuf[i].key < ctx.mapBuf[j].key + }) + + // Flush with sequential indices. + for i, entry := range ctx.mapBuf { + newPrefix := s.currPrefix + "." + strconv.Itoa(i+1) + for _, kv := range entry.values { + s.writeValue(newPrefix+kv.k, kv.v) + } + } + + s.currPrefix = ctx.prefix +} + +// WriteBigInteger is unimplemented. +func (s *ShapeSerializer) WriteBigInteger(_ *smithy.Schema, _ big.Int) { + panic("query: BigInteger not supported") +} + +// WriteBigDecimal is unimplemented. +func (s *ShapeSerializer) WriteBigDecimal(_ *smithy.Schema, _ big.Float) { + panic("query: BigDecimal not supported") +} + +// WriteDocument is unimplemented. +func (s *ShapeSerializer) WriteDocument(_ *smithy.Schema, _ document.Value) { + panic("query: Document not supported") +} + +func (s *ShapeSerializer) consumeMapValue() string { + ctx := s.pop() + valPrefix := s.currPrefix + s.currPrefix = ctx.prefix + return valPrefix +} + +func ec2MemberName(schema *smithy.Schema) string { + if t, ok := smithy.SchemaTrait[*traits.EC2QueryName](schema); ok { + return t.Name + } + if xn, ok := smithy.SchemaTrait[*traits.XMLName](schema); ok { + return capitalize(xn.Name) + } + return capitalize(schema.MemberName()) +} + +func capitalize(s string) string { + if s == "" || s[0] >= 'A' && s[0] <= 'Z' { + return s + } + + return string(s[0]-'a'+'A') + s[1:] +} diff --git a/aws-protocols/internal/xml/error.go b/aws-protocols/internal/xml/error.go new file mode 100644 index 000000000..829b05977 --- /dev/null +++ b/aws-protocols/internal/xml/error.go @@ -0,0 +1,41 @@ +package xml + +import "io" + +// GetProtocolErrorInfo extracts Smithy error details from a query-protocol +// response. +// +// This version of GetProtocolErrorInfo also handles the extraction of the +// modeled error body from the protocol wrapper so the caller can operate on it +// directly. +func GetProtocolErrorInfo(payload []byte) (code, message string, errorBody []byte, err error) { + code = "UnknownError" + message = code + + errorBody, err = ExtractElement(payload, "Error") + if err != nil || len(errorBody) == 0 { + return + } + + // we are in a fragment here so just leverage ExtractElement to get the + // inner XML of these, instead of having to wrap in a tag to pass to the + // stdlib decoder + c, err := ExtractElement(errorBody, "Code") + if err != nil && err != io.EOF { + return + } + m, err := ExtractElement(errorBody, "Message") + if err != nil && err != io.EOF { + return + } + + // clear err if it was io.EOF + err = nil + if len(c) > 0 { + code = string(c) + } + if len(m) > 0 { + message = string(m) + } + return +} diff --git a/aws-protocols/internal/xml/extract.go b/aws-protocols/internal/xml/extract.go new file mode 100644 index 000000000..1c8470072 --- /dev/null +++ b/aws-protocols/internal/xml/extract.go @@ -0,0 +1,72 @@ +package xml + +import ( + "bytes" + "encoding/xml" + "strings" +) + +// ExtractElement finds the first occurrence of the named element in the XML +// and returns its INNER content as raw bytes. +// +// This is used to extract both success and error responses from the protocol +// XML that wraps them. +// +// e.g. for awsquery, with an operation output shape named XmlListsResult: +// +// +// +// foo +// ... +// +// +// +// ExtractElement gives you "foo...". +// +// ExtractElement will forward the io.EOF returned by the underlying decoder if +// the target element is not found, which the caller can index on if they're +// looking for an optional element. +func ExtractElement(payload []byte, name string) ([]byte, error) { + dec := xml.NewDecoder(bytes.NewReader(payload)) + for { + tok, err := dec.Token() + if err != nil { + return nil, err + } + + if start, ok := tok.(xml.StartElement); ok { + if strings.EqualFold(start.Name.Local, name) { + return extract(dec) + } + } + } +} + +func extract(dec *xml.Decoder) ([]byte, error) { + var buf bytes.Buffer + enc := xml.NewEncoder(&buf) + depth := 1 + + for depth > 0 { + tok, err := dec.Token() + if err != nil { + return nil, err + } + + switch t := tok.(type) { + case xml.StartElement: + depth++ + enc.EncodeToken(t) + case xml.EndElement: + depth-- + if depth > 0 { + enc.EncodeToken(t) + } + case xml.CharData: + enc.EncodeToken(t) + } + } + + enc.Flush() + return buf.Bytes(), nil +} diff --git a/aws-protocols/internal/xml/shape_deserializer.go b/aws-protocols/internal/xml/shape_deserializer.go new file mode 100644 index 000000000..aac38f1f7 --- /dev/null +++ b/aws-protocols/internal/xml/shape_deserializer.go @@ -0,0 +1,686 @@ +package xml + +import ( + "bytes" + "encoding/base64" + "encoding/xml" + "fmt" + "io" + "math" + "strconv" + "strings" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + smithytime "github.com/aws/smithy-go/time" + "github.com/aws/smithy-go/traits" +) + +type ctxKind byte + +const ( + ctxKindStruct ctxKind = iota + ctxKindList + ctxKindMap +) + +type deserCtx struct { + kind ctxKind + schema *smithy.Schema + flattened bool + + // for flattened list/map, ReadStructMember consumes the start element + // when it discovers the member, subsequent calls to ReadMapKey or + // ReadListItem will do that instead + first bool + + // for map, ReadX after ReadMapKey will leave the stream at + // which the next call to ReadMapKey must consume + inMapEntry bool +} + +// ShapeDeserializer implements unmarshaling of XML into Smithy shapes. +// +// ShapeDeserializer expects the **inner XML** of the protocol response that +// represents the operation output. For example, an awsquery response body +// looks like this: +// +// <[OperationName]Response> +// <[OperationName]Result> +// ... +// ... +// ... +// ... +// +// +// ... +// +// +// +// The deserializer must receive "..." to operate correctly. +type ShapeDeserializer struct { + dec *xml.Decoder + peeked xml.Token + stack []deserCtx +} + +var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil) + +// NewShapeDeseralizer returns a new ShapeDeserializer. +func NewShapeDeserializer(p []byte) *ShapeDeserializer { + return &ShapeDeserializer{ + dec: xml.NewDecoder(bytes.NewReader(p)), + } +} + +func (d *ShapeDeserializer) push(ctx deserCtx) { + d.stack = append(d.stack, ctx) +} + +func (d *ShapeDeserializer) pop() deserCtx { + n := len(d.stack) + v := d.stack[n-1] + d.stack = d.stack[:n-1] + return v +} + +func (d *ShapeDeserializer) top() *deserCtx { + if len(d.stack) == 0 { + return nil + } + return &d.stack[len(d.stack)-1] +} + +func xmlMemberName(schema *smithy.Schema) string { + if t, ok := smithy.SchemaTrait[*traits.XMLName](schema); ok { + return t.Name + } + return schema.MemberName() +} + +func findMember(schema *smithy.Schema, elemName string) *smithy.Schema { + if schema == nil { + return nil + } + + for _, m := range schema.Members() { + mName := xmlMemberName(m) + if strings.EqualFold(mName, elemName) { + return m + } + } + return nil +} + +// ReadStruct implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error { + d.push(deserCtx{kind: ctxKindStruct, schema: s}) + return nil +} + +// ReadStructMember implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { + ctx := d.top() + if ctx == nil || ctx.kind != ctxKindStruct { + return nil, fmt.Errorf("ReadStructMember called without ReadStruct") + } + + for { + start, ok, err := d.nextStart() + if err != nil { + // on the top-level struct we are guaranteed to get an EOF since + // we're operating on inner XML + if err == io.EOF && len(d.stack) == 1 { + d.pop() + return nil, nil + } + + return nil, err + } + if !ok { + d.pop() + return nil, nil + } + + member := findMember(ctx.schema, start.Name.Local) + if member == nil { + if err := d.skip(); err != nil { + return nil, err + } + continue + } + + return member, nil + } +} + +// ReadList implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { + _, flattened := smithy.SchemaTrait[*traits.XMLFlattened](s) + d.push(deserCtx{ + kind: ctxKindList, + schema: s, + flattened: flattened, + first: flattened, + }) + return nil +} + +// ReadListItem implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadListItem(_ *smithy.Schema) (bool, error) { + ctx := d.top() + if ctx.flattened { + return d.readFlatListItem() + } + return d.readWrappedListItem() +} + +func (d *ShapeDeserializer) readWrappedListItem() (bool, error) { + // the old version used WrapNodeDecoder on each item and didn't check its + // name (or for xmlName) so we're ignoring it too + _, ok, err := d.nextStart() + if err != nil { + return false, err + } + if !ok { + d.pop() + return false, nil + } + + return true, nil +} + +func (d *ShapeDeserializer) readFlatListItem() (bool, error) { + ctx := d.top() + if ctx.first { + ctx.first = false + return true, nil + } + + expectName := xmlMemberName(ctx.schema) + + for { + tok, err := d.token() + if err != nil { + return false, err + } + + switch t := tok.(type) { + case xml.StartElement: + if strings.EqualFold(t.Name.Local, expectName) { + return true, nil + } + + d.peeked = t + d.pop() + return false, nil + case xml.EndElement: + d.peeked = t + d.pop() + return false, nil + } + } +} + +// ReadMap implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadMap(s *smithy.Schema) error { + _, flattened := smithy.SchemaTrait[*traits.XMLFlattened](s) + d.push(deserCtx{ + kind: ctxKindMap, + schema: s, + flattened: flattened, + first: flattened, + }) + return nil +} + +// ReadMapKey implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadMapKey(ks *smithy.Schema) (string, bool, error) { + ctx := d.top() + if ctx.inMapEntry { + ctx.inMapEntry = false + if _, _, err := d.nextStart(); err != nil { + return "", false, err + } + } + + vs := ctx.schema.MapValue() + + if ctx.flattened { + return d.readFlatMapKey(ks, vs) + } + + return d.readWrappedMapKey(ks, vs) +} + +func (d *ShapeDeserializer) readWrappedMapKey(ks, vs *smithy.Schema) (string, bool, error) { + for { + start, a, err := d.nextStart() + if err != nil { + return "", false, err + } + if !a { + d.pop() + return "", false, nil + } + + // unlike lists the old codegen actually DID check that the element was + // named "entry", skipping if it wasn't + if strings.EqualFold(start.Name.Local, "entry") { + return d.readEntry(ks, vs) + } + + if err := d.skip(); err != nil { + return "", false, err + } + } +} + +func (d *ShapeDeserializer) readFlatMapKey(ks, vs *smithy.Schema) (string, bool, error) { + ctx := d.top() + if ctx.first { + ctx.first = false + return d.readEntry(ks, vs) + } + + expectName := xmlMemberName(ctx.schema) + for { + tok, err := d.token() + if err != nil { + return "", false, err + } + + switch t := tok.(type) { + case xml.StartElement: + if strings.EqualFold(t.Name.Local, expectName) { + return d.readEntry(ks, vs) + } + + d.peeked = t + d.pop() + return "", false, nil + case xml.EndElement: + d.peeked = t + d.pop() + return "", false, nil + } + } +} + +func (d *ShapeDeserializer) readEntry(ks, vs *smithy.Schema) (string, bool, error) { + ctx := d.top() + + kname := "key" + if xn, ok := smithy.SchemaTrait[*traits.XMLName](ks); ok { + kname = xn.Name + } + + vname := "value" + if xn, ok := smithy.SchemaTrait[*traits.XMLName](vs); ok { + vname = xn.Name + } + + var key string + for { + child, found, err := d.nextStart() + if err != nil { + return "", false, err + } + if !found { + break + } + + switch { + case strings.EqualFold(child.Name.Local, kname): + key, err = d.chardata() + if err != nil { + return "", false, err + } + case strings.EqualFold(child.Name.Local, vname): + ctx.inMapEntry = true + return key, true, nil + default: + if err := d.skip(); err != nil { + return "", false, err + } + } + } + + return key, true, nil +} + +// ReadNil implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadNil(_ *smithy.Schema) (bool, error) { + return false, nil +} + +// ReadBool implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadBool(_ *smithy.Schema, v *bool) error { + text, err := d.chardata() + if err != nil { + return err + } + + b, err := strconv.ParseBool(text) + if err != nil { + return fmt.Errorf("parse bool %q: %w", text, err) + } + + *v = b + return nil +} + +// ReadBoolPtr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadBoolPtr(s *smithy.Schema, v **bool) error { + return readPtr(d, s, v, d.ReadBool) +} + +// ReadInt8 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt8(s *smithy.Schema, v *int8) error { + n, err := d.readInt(8) + if err != nil { + return err + } + + *v = int8(n) + return nil +} + +// ReadInt16 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt16(s *smithy.Schema, v *int16) error { + n, err := d.readInt(16) + if err != nil { + return err + } + + *v = int16(n) + return nil +} + +// ReadInt32 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt32(s *smithy.Schema, v *int32) error { + n, err := d.readInt(32) + if err != nil { + return err + } + + *v = int32(n) + return nil +} + +// ReadInt64 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { + n, err := d.readInt(64) + if err != nil { + return err + } + + *v = n + return nil +} + +// ReadInt8Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt8Ptr(s *smithy.Schema, v **int8) error { + return readPtr(d, s, v, d.ReadInt8) +} + +// ReadInt16Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt16Ptr(s *smithy.Schema, v **int16) error { + return readPtr(d, s, v, d.ReadInt16) +} + +// ReadInt32Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt32Ptr(s *smithy.Schema, v **int32) error { + return readPtr(d, s, v, d.ReadInt32) +} + +// ReadInt64Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadInt64Ptr(s *smithy.Schema, v **int64) error { + return readPtr(d, s, v, d.ReadInt64) +} + +// ReadFloat32 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error { + n, err := d.readFloat() + if err != nil { + return err + } + + *v = float32(n) + return nil +} + +// ReadFloat64 implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { + n, err := d.readFloat() + if err != nil { + return err + } + + *v = n + return nil +} + +// ReadFloat32Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat32Ptr(s *smithy.Schema, v **float32) error { + return readPtr(d, s, v, d.ReadFloat32) +} + +// ReadFloat64Ptr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadFloat64Ptr(s *smithy.Schema, v **float64) error { + return readPtr(d, s, v, d.ReadFloat64) +} + +// ReadString implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadString(_ *smithy.Schema, v *string) error { + text, err := d.chardata() + if err != nil { + return err + } + + *v = text + return nil +} + +// ReadStringPtr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadStringPtr(s *smithy.Schema, v **string) error { + return readPtr(d, s, v, d.ReadString) +} + +// ReadTime implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadTime(schema *smithy.Schema, v *time.Time) error { + format := "date-time" + if t, ok := smithy.SchemaTrait[*traits.TimestampFormat](schema); ok { + format = t.Format + } + + text, err := d.chardata() + if err != nil { + return err + } + + switch format { + case "date-time": + t, err := smithytime.ParseDateTime(text) + if err != nil { + return err + } + *v = t + case "http-date": + t, err := smithytime.ParseHTTPDate(text) + if err != nil { + return err + } + *v = t + case "epoch-seconds": + n, err := strconv.ParseFloat(text, 64) + if err != nil { + return err + } + *v = smithytime.ParseEpochSeconds(n) + default: + return fmt.Errorf("unknown timestamp format: %s", format) + } + + return nil +} + +// ReadTimePtr implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadTimePtr(schema *smithy.Schema, v **time.Time) error { + return readPtr(d, schema, v, d.ReadTime) +} + +// ReadBlob implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { + text, err := d.chardata() + if err != nil { + return err + } + + if text == "" { + return nil + } + + b, err := base64.StdEncoding.DecodeString(text) + if err != nil { + return fmt.Errorf("decode base64 blob: %w", err) + } + *v = b + return nil +} + +// ReadUnion implements [smithy.ShapeDeserializer]. +func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) { + start, ok, err := d.nextStart() + if err != nil { + return nil, err + } + if !ok { + return nil, nil + } + + member := findMember(s, start.Name.Local) + if member == nil { + if err := d.skip(); err != nil { + return nil, err + } + return d.ReadUnion(s) + } + + return member, nil +} + +// ReadDocument is unimplemented for XML. +func (d *ShapeDeserializer) ReadDocument(_ *smithy.Schema, _ *document.Value) error { + return fmt.Errorf("Document not supported") +} + +func readPtr[T any](d *ShapeDeserializer, s *smithy.Schema, v **T, read func(*smithy.Schema, *T) error) error { + if *v == nil { + *v = new(T) + } + return read(s, *v) +} + +func (d *ShapeDeserializer) token() (xml.Token, error) { + if d.peeked != nil { + tok := d.peeked + d.peeked = nil + return tok, nil + } + + return d.dec.Token() +} + +func (d *ShapeDeserializer) chardata() (string, error) { + // a single "inner XML" node can be multiple xml.CharData so we need to + // accumulate them + var buf strings.Builder + + for { + tok, err := d.token() + if err != nil { + return "", err + } + + switch t := tok.(type) { + case xml.CharData: + buf.Write(t) + + // IMPORTANT: also consumes the closing tag AFTER the chardata, so + // future ReadWhatevers don't have to think about that + case xml.EndElement: + return buf.String(), nil + + default: + return "", fmt.Errorf("unexpected token %T", tok) + } + } +} + +// there is xml.Decoder.Skip but this is a special case to assume we already +// consumed the start element and to handle any peeked tokens +func (d *ShapeDeserializer) skip() error { + depth := 1 + for depth > 0 { + tok, err := d.token() + if err != nil { + return err + } + + switch tok.(type) { + case xml.StartElement: + depth++ + case xml.EndElement: + depth-- + } + } + + return nil +} + +func (d *ShapeDeserializer) nextStart() (xml.StartElement, bool, error) { + for { + tok, err := d.token() + if err != nil { + return xml.StartElement{}, false, err + } + + switch t := tok.(type) { + case xml.StartElement: + return t, true, nil + case xml.EndElement: // ie. the end of the struct/list/map + return xml.StartElement{}, false, nil + default: + continue + } + } +} + +func (d *ShapeDeserializer) readInt(bits int) (int64, error) { + text, err := d.chardata() + if err != nil { + return 0, err + } + + return strconv.ParseInt(text, 10, bits) +} + +func (d *ShapeDeserializer) readFloat() (float64, error) { + text, err := d.chardata() + if err != nil { + return 0, err + } + + switch { + case strings.EqualFold(text, "NaN"): + return math.NaN(), nil + case strings.EqualFold(text, "Infinity"): + return math.Inf(1), nil + case strings.EqualFold(text, "-Infinity"): + return math.Inf(-1), nil + default: + return strconv.ParseFloat(text, 64) + } +} \ No newline at end of file diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java index 1eedc167f..628621f5b 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Map; import software.amazon.smithy.aws.traits.protocols.AwsQueryErrorTrait; +import software.amazon.smithy.aws.traits.protocols.Ec2QueryNameTrait; import software.amazon.smithy.go.codegen.trait.BackfilledInputOutputTrait; import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.traits.EventHeaderTrait; @@ -83,6 +84,8 @@ public class DefaultTraitGenerators { GENERATORS.put(AwsQueryErrorTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("AWSQueryError"), "ErrorCode", AwsQueryErrorTrait::getCode, "StatusCode", AwsQueryErrorTrait::getHttpResponseCode)); + GENERATORS.put(Ec2QueryNameTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("EC2QueryName"), + "Name", Ec2QueryNameTrait::getValue)); // Codegen-internal traits GENERATORS.put(BackfilledInputOutputTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("UnitShape"))); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java index dbbdebe73..c3eb0c9da 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java @@ -31,6 +31,8 @@ import software.amazon.smithy.aws.traits.protocols.AwsJson1_0Trait; import software.amazon.smithy.aws.traits.protocols.AwsJson1_1Trait; import software.amazon.smithy.aws.traits.protocols.AwsQueryCompatibleTrait; +import software.amazon.smithy.aws.traits.protocols.AwsQueryTrait; +import software.amazon.smithy.aws.traits.protocols.Ec2QueryTrait; import software.amazon.smithy.aws.traits.protocols.RestJson1Trait; import software.amazon.smithy.codegen.core.CodegenException; import software.amazon.smithy.codegen.core.Symbol; @@ -48,6 +50,7 @@ import software.amazon.smithy.go.codegen.integration.OperationMetricsStruct; import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin; import software.amazon.smithy.model.Model; +import software.amazon.smithy.protocol.traits.Rpcv2CborTrait; import software.amazon.smithy.model.knowledge.ServiceIndex; import software.amazon.smithy.model.knowledge.TopDownIndex; import software.amazon.smithy.model.shapes.ServiceShape; @@ -285,6 +288,16 @@ private Writable resolveDefaultProtocol() { ? goTemplate("$T", SmithyGoDependency.SMITHY_HTTP_PROTOCOLS_RPCV2.valueSymbol("UseQueryCompatible")) : emptyGoTemplate() ); + } else if (preferred.equals(AwsQueryTrait.ID)) { + return goTemplate("$T($S)", + SmithyGoDependency.SMITHY_AWS_PROTOCOLS_AWSQUERY.func("New"), + service.getVersion() + ); + } else if (preferred.equals(Ec2QueryTrait.ID)) { + return goTemplate("$T($S)", + SmithyGoDependency.SMITHY_AWS_PROTOCOLS_EC2QUERY.func("New"), + service.getVersion() + ); } else { throw new CodegenException("unsupported schema-serde protocol " + preferred); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SimpleTraitGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SimpleTraitGenerator.java index 5abcc3913..49accd5a2 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SimpleTraitGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SimpleTraitGenerator.java @@ -4,6 +4,7 @@ import java.util.LinkedHashMap; import java.util.Map; +import java.util.Optional; import java.util.function.Function; import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.model.traits.Trait; @@ -47,6 +48,12 @@ public SimpleTraitGenerator(Symbol symbol, public Writable render(Trait trait) { return goTemplate("&$T{$W}", symbol, Writable.map(mappings.entrySet(), entry -> { var value = entry.getValue().apply((T) trait); + if (value instanceof Optional opt) { + if (opt.isEmpty()) { + return GoWriter.emptyGoTemplate(); + } + value = opt.get(); + } return goTemplate("$L: $L,", entry.getKey(), formatValue(value)); })); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java index 5002c43fe..1fdf542b5 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java @@ -85,6 +85,8 @@ public final class SmithyGoDependency { public static final GoDependency SMITHY_AWS_PROTOCOLS = relativePackage( "github.com/aws/smithy-go/aws-protocols", null, "v1.0.0", null); public static final GoDependency SMITHY_AWS_PROTOCOLS_JSON10 = relativePackage( "github.com/aws/smithy-go/aws-protocols", "awsjson10", "v1.0.0", null); public static final GoDependency SMITHY_AWS_PROTOCOLS_RESTJSON1 = relativePackage( "github.com/aws/smithy-go/aws-protocols", "restjson1", "v1.0.0", null); + public static final GoDependency SMITHY_AWS_PROTOCOLS_AWSQUERY = relativePackage( "github.com/aws/smithy-go/aws-protocols", "awsquery", "v1.0.0", null); + public static final GoDependency SMITHY_AWS_PROTOCOLS_EC2QUERY = relativePackage( "github.com/aws/smithy-go/aws-protocols", "ec2query", "v1.0.0", null); public static final GoDependency SMITHY_HTTP_PROTOCOLS = relativePackage( "github.com/aws/smithy-go/smithy-http-protocols", null, "v1.0.0", null); public static final GoDependency SMITHY_HTTP_PROTOCOLS_RPCV2 = relativePackage( "github.com/aws/smithy-go/smithy-http-protocols", "rpcv2", "v1.0.0", null); diff --git a/eventstream/no_event_stream.go b/eventstream/no_event_stream.go new file mode 100644 index 000000000..3862cdb53 --- /dev/null +++ b/eventstream/no_event_stream.go @@ -0,0 +1,36 @@ +package eventstream + +import ( + "fmt" + "io" + + "github.com/aws/smithy-go" +) + +// NoEventStream implements the event stream methods of +// [github.com/aws/smithy-go/transport/http.ClientProtocol] for protocols +// that do not support event streams. +type NoEventStream struct{} + +// HasInitialEventMessage returns false. +func (NoEventStream) HasInitialEventMessage() bool { return false } + +// SerializeEventMessage returns an error. +func (NoEventStream) SerializeEventMessage(_, _ *smithy.Schema, _ smithy.Serializable, _ io.Writer) error { + return fmt.Errorf("event streams are not supported by this protocol") +} + +// DeserializeEventMessage returns an error. +func (NoEventStream) DeserializeEventMessage(_ *smithy.Schema, _ *smithy.TypeRegistry, _ io.Reader) (smithy.Deserializable, error) { + return nil, fmt.Errorf("event streams are not supported by this protocol") +} + +// SerializeInitialRequest returns an error. +func (NoEventStream) SerializeInitialRequest(_ *smithy.Schema, _ smithy.Serializable, _ io.Writer) error { + return fmt.Errorf("event streams are not supported by this protocol") +} + +// DeserializeInitialResponse returns an error. +func (NoEventStream) DeserializeInitialResponse(_ *smithy.Schema, _ io.Reader, _ smithy.Deserializable) error { + return fmt.Errorf("event streams are not supported by this protocol") +} diff --git a/serde.go b/serde.go index c0fb6d6ab..a440b18e7 100644 --- a/serde.go +++ b/serde.go @@ -196,6 +196,11 @@ func ReadStruct(d ShapeDeserializer, schema *Schema, memberFn func(*Schema) erro return nil } + // TODO(serde2): err check should be first. ran into this with XML + // because of how that shape deserializer operates on innerXML, you get + // a guaranteed EOF in the end, but I have explicitly swallowed that + // EOF there when it's on the outer struct since that's how that + // deserializer has to work if err != nil { return err } diff --git a/trait.go b/trait.go index 3e9768737..a70b10abc 100644 --- a/trait.go +++ b/trait.go @@ -6,3 +6,7 @@ package smithy type Trait interface { TraitID() string } + +// TODO(serde2): investigate performance tradeoff of using an "indexed" map for +// the known traits (basically the ones defined here) since the rest- and +// xml-based protocols do a ton of trait lookup (which translates to map lookup) diff --git a/traits/traits.go b/traits/traits.go index 55d26f58c..932bf5ba2 100644 --- a/traits/traits.go +++ b/traits/traits.go @@ -47,6 +47,14 @@ type AWSQueryError struct { // TraitID identifies the trait. func (*AWSQueryError) TraitID() string { return "aws.protocols#awsQueryError" } +// EC2QueryName represents aws.protocols#ec2QueryName. +type EC2QueryName struct { + Name string +} + +// TraitID identifies the trait. +func (*EC2QueryName) TraitID() string { return "aws.protocols#ec2QueryName" } + // UnitShape is a synthetic trait applied to input/output shapes that were // backfilled from Unit. It indicates the shape has no defined members and // should be treated as absent for protocol serialization purposes. From 7b86c135d7a1a3a107ac772ae855ca433af53a66 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Mon, 27 Apr 2026 14:09:47 -0400 Subject: [PATCH 10/38] fix compile err --- aws-protocols/internal/json/shape_deserializer.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/aws-protocols/internal/json/shape_deserializer.go b/aws-protocols/internal/json/shape_deserializer.go index e3e050757..cb983a933 100644 --- a/aws-protocols/internal/json/shape_deserializer.go +++ b/aws-protocols/internal/json/shape_deserializer.go @@ -488,13 +488,9 @@ func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) // skip null values isNil, err := d.ReadNil(nil) if err != nil { - return nil, err - } else { - continue + return nil, err } - if err != nil { - return nil, err - } + if isNil { continue } From 154c6a1d81ddaea40a3b610ad9343496f56049ad Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Thu, 30 Apr 2026 11:54:17 -0400 Subject: [PATCH 11/38] switch to explicit WriteStruct (#655) --- aws-protocols/go.mod | 7 -- aws-protocols/go.sum | 0 .../internal/httpbinding/serializer.go | 71 ++++++++++--------- .../internal/json/shape_serializer.go | 23 +++--- .../internal/query/shape_serializer.go | 27 +++---- .../smithy/go/codegen/SmithyGoDependency.java | 14 ++-- .../smithy/go/codegen/UnionGenerator.java | 2 +- .../go/codegen/serde2/ListSerializer.java | 4 +- .../go/codegen/serde2/MapSerializer.java | 4 +- .../codegen/serde2/StructureSerializer.java | 11 ++- eventstream/serializer.go | 7 +- serde.go | 3 +- smithy-http-protocols/go.mod | 7 -- smithy-http-protocols/go.sum | 0 .../internal/cbor/shape_serializer.go | 15 ++-- 15 files changed, 99 insertions(+), 96 deletions(-) delete mode 100644 aws-protocols/go.mod delete mode 100644 aws-protocols/go.sum delete mode 100644 smithy-http-protocols/go.mod delete mode 100644 smithy-http-protocols/go.sum diff --git a/aws-protocols/go.mod b/aws-protocols/go.mod deleted file mode 100644 index ed1370766..000000000 --- a/aws-protocols/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module github.com/aws/smithy-go/aws-protocols - -go 1.24 - -require github.com/aws/smithy-go v1.24.0 - -replace github.com/aws/smithy-go => ../ diff --git a/aws-protocols/go.sum b/aws-protocols/go.sum deleted file mode 100644 index e69de29bb..000000000 diff --git a/aws-protocols/internal/httpbinding/serializer.go b/aws-protocols/internal/httpbinding/serializer.go index fd31c49e1..6510af0be 100644 --- a/aws-protocols/internal/httpbinding/serializer.go +++ b/aws-protocols/internal/httpbinding/serializer.go @@ -642,14 +642,40 @@ func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { return } - // TODO(serde2): there is some weirdness going on here because the - // generated Serialize() methods use WriteMap to open their structure - // declaration. So we need to check if that's what's going on here. - // - // ShapeSerializer.WriteStruct is specifically for basically just - // delegating to the provided Serializable. We're probably going to have to - // split out to Serialize + SerializeFields so we can differentiate. It - // looks like smithy-java did something like this. + s.input.WriteMap(schema) +} + +// WriteKey implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteKey(schema *smithy.Schema, key string) { + switch s.mapMode { + case mapModePrefixHeaders, mapModeQueryParams: + s.currentKey = key + default: + s.input.WriteKey(schema, key) + } +} + +// CloseMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseMap() { + if s.mapMode != mapModeNone { + s.mapMode = mapModeNone + s.mapPrefix = "" + s.currentKey = "" + return + } + s.input.CloseMap() +} + +// WriteStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema) { + if schema.MemberName() != "" { // the root + if isHTTPPayload(schema) { + s.hasStructPayload = true + } + s.input.WriteStruct(schema) + return + } + for _, m := range schema.Members() { if !isHTTPPayload(m) { continue @@ -670,37 +696,16 @@ func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { return } - s.input.WriteMap(schema) + s.input.WriteStruct(schema) } -// WriteKey implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteKey(schema *smithy.Schema, key string) { - switch s.mapMode { - case mapModePrefixHeaders, mapModeQueryParams: - s.currentKey = key - default: - s.input.WriteKey(schema, key) - } -} - -// CloseMap implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) CloseMap() { - if s.mapMode != mapModeNone { - s.mapMode = mapModeNone - s.mapPrefix = "" - s.currentKey = "" - return - } +// CloseStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseStruct() { if s.noBody { s.noBody = false return } - s.input.CloseMap() -} - -// WriteStruct implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema, v smithy.Serializable) { - s.input.WriteStruct(schema, v) + s.input.CloseStruct() } // WriteUnion implements [smithy.ShapeSerializer]. diff --git a/aws-protocols/internal/json/shape_serializer.go b/aws-protocols/internal/json/shape_serializer.go index 3e05a9879..38b0d16d4 100644 --- a/aws-protocols/internal/json/shape_serializer.go +++ b/aws-protocols/internal/json/shape_serializer.go @@ -428,19 +428,26 @@ func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema, v smithy.Se } // WriteStruct implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema, v smithy.Serializable) { - if v == nil { - return - } - +func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema) { switch enc := s.head.Top().(type) { case *smithyjson.Object: - s.head.Push(enc.Key(s.jsonMemberName(schema))) + s.head.Push(enc.Key(s.jsonMemberName(schema)).Object()) case *smithyjson.Array: - s.head.Push(enc.Value()) + s.head.Push(enc.Value().Object()) + case smithyjson.Value: + s.head.Pop() + s.head.Push(enc.Object()) + default: + s.head.Push(s.root.Object()) } +} - v.Serialize(s) +// CloseStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseStruct() { + if enc, ok := s.head.Top().(*smithyjson.Object); ok { + enc.Close() + s.head.Pop() + } } // WriteNil implements [smithy.ShapeSerializer]. diff --git a/aws-protocols/internal/query/shape_serializer.go b/aws-protocols/internal/query/shape_serializer.go index 635996945..53513d768 100644 --- a/aws-protocols/internal/query/shape_serializer.go +++ b/aws-protocols/internal/query/shape_serializer.go @@ -382,11 +382,7 @@ func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { } // WriteStruct implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema, v smithy.Serializable) { - if v == nil { - return - } - +func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema) { saved := s.currPrefix switch s.top() { case ctxKindMapValue: @@ -400,8 +396,13 @@ func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema, v smithy.Serializab s.appendMemberPrefix(schema) } - v.Serialize(s) - s.currPrefix = saved + s.push(serCtx{kind: ctxKindStruct, prefix: saved}) +} + +// CloseStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseStruct() { + ctx := s.pop() + s.currPrefix = ctx.prefix } // WriteUnion implements [smithy.ShapeSerializer]. @@ -489,13 +490,6 @@ func (s *ShapeSerializer) CloseList() { // WriteMap implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { - // Structs use WriteMap/CloseMap as a generic "begin object" scope. - if schema != nil && schema.Type() == smithy.ShapeTypeStructure { - saved := s.enterContainer(schema) - s.push(serCtx{kind: ctxKindStruct, prefix: saved}) - return - } - saved := s.enterContainer(schema) _, flattened := smithy.SchemaTrait[*traits.XMLFlattened](schema) @@ -547,11 +541,6 @@ func (s *ShapeSerializer) WriteKey(_ *smithy.Schema, key string) { // CloseMap implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) CloseMap() { - if s.top() == ctxKindStruct { - ctx := s.pop() - s.currPrefix = ctx.prefix - return - } if s.top() != ctxKindMap { return } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java index 1fdf542b5..c4a360721 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java @@ -82,14 +82,14 @@ public final class SmithyGoDependency { public static final GoDependency SMITHY_TRACING = smithy("tracing"); public static final GoDependency SMITHY_METRICS = smithy("metrics"); - public static final GoDependency SMITHY_AWS_PROTOCOLS = relativePackage( "github.com/aws/smithy-go/aws-protocols", null, "v1.0.0", null); - public static final GoDependency SMITHY_AWS_PROTOCOLS_JSON10 = relativePackage( "github.com/aws/smithy-go/aws-protocols", "awsjson10", "v1.0.0", null); - public static final GoDependency SMITHY_AWS_PROTOCOLS_RESTJSON1 = relativePackage( "github.com/aws/smithy-go/aws-protocols", "restjson1", "v1.0.0", null); - public static final GoDependency SMITHY_AWS_PROTOCOLS_AWSQUERY = relativePackage( "github.com/aws/smithy-go/aws-protocols", "awsquery", "v1.0.0", null); - public static final GoDependency SMITHY_AWS_PROTOCOLS_EC2QUERY = relativePackage( "github.com/aws/smithy-go/aws-protocols", "ec2query", "v1.0.0", null); + public static final GoDependency SMITHY_AWS_PROTOCOLS = smithy("aws-protocols"); + public static final GoDependency SMITHY_AWS_PROTOCOLS_JSON10 = smithy("aws-protocols/awsjson10"); + public static final GoDependency SMITHY_AWS_PROTOCOLS_RESTJSON1 = smithy("aws-protocols/restjson1"); + public static final GoDependency SMITHY_AWS_PROTOCOLS_AWSQUERY = smithy("aws-protocols/awsquery"); + public static final GoDependency SMITHY_AWS_PROTOCOLS_EC2QUERY = smithy("aws-protocols/ec2query"); - public static final GoDependency SMITHY_HTTP_PROTOCOLS = relativePackage( "github.com/aws/smithy-go/smithy-http-protocols", null, "v1.0.0", null); - public static final GoDependency SMITHY_HTTP_PROTOCOLS_RPCV2 = relativePackage( "github.com/aws/smithy-go/smithy-http-protocols", "rpcv2", "v1.0.0", null); + public static final GoDependency SMITHY_HTTP_PROTOCOLS = smithy("smithy-http-protocols"); + public static final GoDependency SMITHY_HTTP_PROTOCOLS_RPCV2 = smithy("smithy-http-protocols/rpcv2"); public static final GoDependency MATH = stdlib("math"); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java index 01b55e6a0..72d55a7e8 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java @@ -130,7 +130,7 @@ private void generateMemberSerializer(GoWriter writer, MemberShape member, Strin case INT_ENUM -> writer.write("s.WriteInt32($L, int32(v.Value))", schemaName); case BIG_INTEGER -> writer.write("s.WriteBigInteger($L, v.Value)", schemaName); case BIG_DECIMAL -> writer.write("s.WriteBigDecimal($L, v.Value)", schemaName); - case STRUCTURE -> writer.write("s.WriteStruct($L, &v.Value)", schemaName); // struct variants are value types + case STRUCTURE -> writer.write("s.WriteStruct($L)\nv.Value.SerializeMembers(s)\ns.CloseStruct()", schemaName); // struct variants are value types case LIST, SET, MAP -> writer.write("serialize$L(s, $L, v.Value)", target.getId().getName(), schemaName); case DOCUMENT -> { writer.addUseImports(SmithyGoDependency.SMITHY_DOCUMENT); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java index 0b4d65801..fe3a04af1 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java @@ -96,7 +96,7 @@ private Writable renderSparseSerializeValue() { case LIST, SET, MAP, UNION -> wrapNilCheck(goTemplate("serialize$L(s, schema.ListMember(), vv)", member.getId().getName())); case STRUCTURE -> - wrapNilCheck(goTemplate("vv.Serialize(s)")); + wrapNilCheck(goTemplate("s.WriteStruct(schema.ListMember())\nvv.SerializeMembers(s)\ns.CloseStruct()")); case DOCUMENT -> wrapNilCheck(goTemplate("s.WriteDocument(schema.ListMember(), &smithydocument.Opaque{Value: vv})")); @@ -135,7 +135,7 @@ private Writable renderDenseSerializeValue() { case LIST, SET, MAP, UNION -> goTemplate("serialize$L(s, schema.ListMember(), vv)", member.getId().getName()); case STRUCTURE -> - goTemplate("vv.Serialize(s)"); + goTemplate("s.WriteStruct(schema.ListMember())\nvv.SerializeMembers(s)\ns.CloseStruct()"); case DOCUMENT -> goTemplate("s.WriteDocument(schema.ListMember(), &smithydocument.Opaque{Value: vv})"); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java index f2232b177..21d05df8c 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java @@ -97,7 +97,7 @@ private Writable renderSparseSerializeValue() { case LIST, SET, MAP, UNION -> wrapNilCheck(goTemplate("serialize$L(s, schema.MapValue(), vv)", value.getId().getName())); case STRUCTURE -> - wrapNilCheck(goTemplate("vv.Serialize(s)")); + wrapNilCheck(goTemplate("s.WriteStruct(schema.MapValue())\nvv.SerializeMembers(s)\ns.CloseStruct()")); case DOCUMENT -> wrapNilCheck(goTemplate("s.WriteDocument(schema.MapValue(), &smithydocument.Opaque{Value: vv})")); @@ -136,7 +136,7 @@ private Writable renderDenseSerializeValue() { case LIST, SET, MAP, UNION -> goTemplate("serialize$L(s, schema.MapValue(), vv)", value.getId().getName()); case STRUCTURE -> - goTemplate("vv.Serialize(s)"); + goTemplate("s.WriteStruct(schema.MapValue())\nvv.SerializeMembers(s)\ns.CloseStruct()"); case DOCUMENT -> goTemplate("s.WriteDocument(schema.MapValue(), &smithydocument.Opaque{Value: vv})"); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java index def9e1729..bd53b3639 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java @@ -40,14 +40,19 @@ public void accept(GoWriter writer) { .sorted(Comparator.comparing(MemberShape::getMemberName)) .toList(); writer.addUseImports(SmithyGoDependency.SMITHY); + var schemaRef = "schemas." + SchemaGenerator.getSchemaName(shape, ctx.service()); writer.openBlock("func (v *$L) Serialize(s smithy.ShapeSerializer) {", "}", symbol.getName(), () -> { - writer.write("s.WriteMap(schemas.$L)", SchemaGenerator.getSchemaName(shape, ctx.service())); + writer.write("s.WriteStruct($L)", schemaRef); + writer.write("v.SerializeMembers(s)"); + writer.write("s.CloseStruct()"); + }); + writer.write(""); + writer.openBlock("func (v *$L) SerializeMembers(s smithy.ShapeSerializer) {", "}", symbol.getName(), () -> { for (var member : members) { var target = ShapeUtil.expectMember(ctx.model(), shape, member.getMemberName()); var ident = String.format("v.%s", ctx.symbolProvider().toMemberName(member)); generateSerializeMember(writer, member, target, ident); } - writer.write("s.CloseMap()"); }); writer.write(""); } @@ -86,7 +91,7 @@ private void generateSerializeMember(GoWriter writer, MemberShape member, Shape case LIST, SET, MAP, UNION -> writer.write("serialize$L(s, $L, $L)", target.getId().getName(), schemaName, ident); case STRUCTURE -> - writer.write("if ($2L != nil) { s.WriteStruct($1L, $2L) }", schemaName, ident); + writer.write("if ($2L != nil) { s.WriteStruct($1L)\n$2L.SerializeMembers(s)\ns.CloseStruct() }", schemaName, ident); case DOCUMENT -> { writer.addUseImports(SmithyGoDependency.SMITHY_DOCUMENT); writer.write("s.WriteDocument($L, &smithydocument.Opaque{Value: $L})", schemaName, ident); diff --git a/eventstream/serializer.go b/eventstream/serializer.go index 443951dd2..bf51f727d 100644 --- a/eventstream/serializer.go +++ b/eventstream/serializer.go @@ -191,8 +191,11 @@ func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { } // WriteStruct implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema, v smithy.Serializable) { - v.Serialize(s) +func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema) { +} + +// CloseStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseStruct() { } // WriteUnion implements [smithy.ShapeSerializer]. diff --git a/serde.go b/serde.go index a440b18e7..a23e6f09c 100644 --- a/serde.go +++ b/serde.go @@ -48,7 +48,8 @@ type ShapeSerializer interface { WriteTime(*Schema, time.Time) WriteTimePtr(*Schema, *time.Time) - WriteStruct(*Schema, Serializable) + WriteStruct(*Schema) + CloseStruct() WriteUnion(schema, variant *Schema, v Serializable) diff --git a/smithy-http-protocols/go.mod b/smithy-http-protocols/go.mod deleted file mode 100644 index 56edb0c58..000000000 --- a/smithy-http-protocols/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module github.com/aws/smithy-go/smithy-http-protocols - -go 1.24 - -require github.com/aws/smithy-go v1.24.0 - -replace github.com/aws/smithy-go => ../ diff --git a/smithy-http-protocols/go.sum b/smithy-http-protocols/go.sum deleted file mode 100644 index e69de29bb..000000000 diff --git a/smithy-http-protocols/internal/cbor/shape_serializer.go b/smithy-http-protocols/internal/cbor/shape_serializer.go index 67d7f1306..d619a58be 100644 --- a/smithy-http-protocols/internal/cbor/shape_serializer.go +++ b/smithy-http-protocols/internal/cbor/shape_serializer.go @@ -325,15 +325,22 @@ func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema, v smithy.Se } // WriteStruct implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema, v smithy.Serializable) { - if v == nil { - return +func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema) { + if s.rootSchema == nil && len(s.head) == 0 { + s.rootSchema = schema } s.writeKey(schema) if s.top() == ctxMapValue { s.pop() } - v.Serialize(s) + s.buf = append(s.buf, compose(majorTypeMap, minorIndefinite)) + s.push(ctxMap) +} + +// CloseStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseStruct() { + s.pop() + s.buf = append(s.buf, 0xff) } // WriteNil implements [smithy.ShapeSerializer]. From 05dd5b89d6cfb51a977270f132a46356b88817c5 Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Tue, 5 May 2026 11:30:49 -0400 Subject: [PATCH 12/38] add prelude schemas, fix various codegen issues (#657) --- .../smithy/go/codegen/OperationGenerator.java | 6 +-- .../smithy/go/codegen/SchemaGenerator.java | 40 +++++++++++++- .../smithy/go/codegen/ServiceGenerator.java | 2 +- .../smithy/go/codegen/SmithyGoDependency.java | 1 + .../smithy/go/codegen/UnionGenerator.java | 31 +++++++---- .../go/codegen/serde2/ListDeserializer.java | 16 ++++-- .../go/codegen/serde2/ListSerializer.java | 8 +-- .../go/codegen/serde2/MapDeserializer.java | 16 ++++-- .../go/codegen/serde2/MapSerializer.java | 8 +-- .../serde2/Serde2EventStreamMiddleware.java | 10 ++-- .../codegen/serde2/StructureDeserializer.java | 54 ++++++++++--------- .../codegen/serde2/StructureSerializer.java | 15 +++--- .../go/codegen/serde2/UnionDeserializer.java | 4 +- .../go/codegen/serde2/UnionSerializer.java | 4 +- .../smithy/go/codegen/util/ShapeUtil.java | 7 +++ prelude/prelude.go | 31 +++++++++++ 16 files changed, 179 insertions(+), 74 deletions(-) create mode 100644 prelude/prelude.go diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java index 4785c0cb5..51e196b7e 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java @@ -291,9 +291,9 @@ private void generateOperationProtocolMiddlewareAdders() { writer.write("if err != nil { return err }"); } else { writer.addUseImports(SmithyGoDependency.SMITHY); - var opSchemaName = "schemas." + SchemaGenerator.getSchemaName(operation, service); - var inputSchemaName = "schemas." + SchemaGenerator.getSchemaName(input, service); - var outputSchemaName = "schemas." + SchemaGenerator.getSchemaName(output, service); + var opSchemaName = SchemaGenerator.getSchemaRef(operation, service); + var inputSchemaName = SchemaGenerator.getSchemaRef(input, service); + var outputSchemaName = SchemaGenerator.getSchemaRef(output, service); var opSchema = String.format("smithy.NewOperationSchema(%s, %s, %s)", opSchemaName, inputSchemaName, outputSchemaName); writer.write(""" diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java index 1357f027c..68fbbe12b 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java @@ -5,6 +5,7 @@ import java.util.Map; import software.amazon.smithy.go.codegen.util.ShapeUtil; +import software.amazon.smithy.model.loader.Prelude; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.Shape; @@ -30,6 +31,9 @@ public SchemaGenerator(GoCodegenContext ctx, Shape shape) { private static final String SYNTHETIC_NAMESPACE = "smithy.go.synthetic"; public static String getSchemaName(Shape shape, ServiceShape service) { + if (Prelude.isPublicPreludeShape(shape)) { + return "smithyprelude." + shape.getId().getName(); + } var name = shape.getId().getName(service); if (shape.getId().getNamespace().equals(SYNTHETIC_NAMESPACE)) { name = "SmithyGoSynthetic_" + name; @@ -43,8 +47,28 @@ public static String getMemberSchemaName(Shape shape, MemberShape member, Servic return String.format("%s_%s", getSchemaName(shape, service), member.getMemberName()); } + // Returns the schema reference for use outside the schemas package (e.g. in + // serde generators). Prelude shapes are already package-qualified, local + // shapes get the "schemas." prefix. + public static String getSchemaRef(Shape shape, ServiceShape service) { + if (Prelude.isPublicPreludeShape(shape)) { + return getSchemaName(shape, service); + } + return "schemas." + getSchemaName(shape, service); + } + + public static String getMemberSchemaRef(Shape shape, MemberShape member, ServiceShape service) { + if (Prelude.isPublicPreludeShape(shape)) { + return getSchemaName(shape, service) + "_" + member.getMemberName(); + } + return "schemas." + getMemberSchemaName(shape, member, service); + } + @Override public void accept(GoWriter writer) { + if (Prelude.isPublicPreludeShape(shape)) { + return; + } var service = ctx.service(); writer.addUseImports(SmithyGoDependency.SMITHY); writer.writeGoTemplate(""" @@ -66,7 +90,7 @@ public void accept(GoWriter writer) { } public void acceptMembersInit(GoWriter writer) { - if (shape.members().isEmpty()) { + if (shape.members().isEmpty() || Prelude.isPublicPreludeShape(shape)) { return; } writer.writeGoTemplate(""" @@ -89,6 +113,20 @@ private Writable renderMemberAddAndAssign(MemberShape member) { var service = ctx.service(); var memberTraits = member.getAllTraits().values(); + if (Prelude.isPublicPreludeShape(target)) { + return goTemplate(""" + $preludeImport:D$ident:L = $schema:L.AddMember($name:S, $target:L$traits:W) + """, + Map.of( + "preludeImport", SmithyGoDependency.SMITHY_PRELUDE, + "ident", getMemberSchemaName(shape, member, service), + "schema", getSchemaName(shape, service), + "name", member.getMemberName(), + "target", getSchemaName(target, service), + "traits", renderVariadicTraits(memberTraits) + )); + } + return goTemplate(""" $ident:L = $schema:L.AddMember($name:S, $target:L$traits:W) """, diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java index c3eb0c9da..bf083027e 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java @@ -299,7 +299,7 @@ private Writable resolveDefaultProtocol() { service.getVersion() ); } else { - throw new CodegenException("unsupported schema-serde protocol " + preferred); + return goTemplate("nil"); } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java index c4a360721..0a1be7b1f 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java @@ -70,6 +70,7 @@ public final class SmithyGoDependency { public static final GoDependency SMITHY_TESTING = smithy("testing", "smithytesting"); public static final GoDependency SMITHY_WAITERS = smithy("waiter", "smithywaiter"); public static final GoDependency SMITHY_DOCUMENT = smithy("document", "smithydocument"); + public static final GoDependency SMITHY_PRELUDE = smithy("prelude", "smithyprelude"); public static final GoDependency SMITHY_DOCUMENT_JSON = smithy("document/json", "smithydocumentjson"); public static final GoDependency SMITHY_DOCUMENT_CBOR = smithy("document/cbor", "smithydocumentcbor"); public static final GoDependency SMITHY_SYNC = smithy("sync", "smithysync"); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java index 72d55a7e8..192add0cf 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java @@ -28,6 +28,7 @@ import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.SimpleShape; import software.amazon.smithy.model.shapes.UnionShape; +import software.amazon.smithy.go.codegen.util.ShapeUtil; import software.amazon.smithy.model.traits.ErrorTrait; import software.amazon.smithy.model.traits.StreamingTrait; import software.amazon.smithy.utils.SmithyInternalApi; @@ -113,7 +114,7 @@ public void generateUnion(GoWriter writer) { private void generateMemberSerializer(GoWriter writer, MemberShape member, String memberName, Shape target) { writer.addUseImports(SmithyGoDependency.SMITHY); writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); - var schemaName = "schemas." + SchemaGenerator.getMemberSchemaName(shape, member, ctx.service()); + var schemaName = SchemaGenerator.getMemberSchemaRef(shape, member, ctx.service()); writer.openBlock("func (v *$L) Serialize(s smithy.ShapeSerializer) {", "}", memberName, () -> { switch (target.getType()) { case BYTE -> writer.write("s.WriteInt8($L, v.Value)", schemaName); @@ -123,10 +124,15 @@ private void generateMemberSerializer(GoWriter writer, MemberShape member, Strin case FLOAT -> writer.write("s.WriteFloat32($L, v.Value)", schemaName); case DOUBLE -> writer.write("s.WriteFloat64($L, v.Value)", schemaName); case BOOLEAN -> writer.write("s.WriteBool($L, v.Value)", schemaName); - case STRING -> writer.write("s.WriteString($L, v.Value)", schemaName); + case STRING, ENUM -> { + if (ShapeUtil.isEnum(target)) { + writer.write("s.WriteString($L, string(v.Value))", schemaName); + } else { + writer.write("s.WriteString($L, v.Value)", schemaName); + } + } case BLOB -> writer.write("s.WriteBlob($L, v.Value)", schemaName); case TIMESTAMP -> writer.write("s.WriteTime($L, v.Value)", schemaName); - case ENUM -> writer.write("s.WriteString($L, string(v.Value))", schemaName); case INT_ENUM -> writer.write("s.WriteInt32($L, int32(v.Value))", schemaName); case BIG_INTEGER -> writer.write("s.WriteBigInteger($L, v.Value)", schemaName); case BIG_DECIMAL -> writer.write("s.WriteBigDecimal($L, v.Value)", schemaName); @@ -144,7 +150,7 @@ private void generateMemberSerializer(GoWriter writer, MemberShape member, Strin private void generateMemberDeserializer(GoWriter writer, MemberShape member, String memberName, Shape target) { writer.addUseImports(SmithyGoDependency.SMITHY); writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); - var schemaName = "schemas." + SchemaGenerator.getMemberSchemaName(shape, member, ctx.service()); + var schemaName = SchemaGenerator.getMemberSchemaRef(shape, member, ctx.service()); writer.openBlock("func (v *$L) Deserialize(d smithy.ShapeDeserializer) error {", "}", memberName, () -> { switch (target.getType()) { case BYTE -> writer.write("return d.ReadInt8($L, &v.Value)", schemaName); @@ -154,15 +160,18 @@ private void generateMemberDeserializer(GoWriter writer, MemberShape member, Str case FLOAT -> writer.write("return d.ReadFloat32($L, &v.Value)", schemaName); case DOUBLE -> writer.write("return d.ReadFloat64($L, &v.Value)", schemaName); case BOOLEAN -> writer.write("return d.ReadBool($L, &v.Value)", schemaName); - case STRING -> writer.write("return d.ReadString($L, &v.Value)", schemaName); + case STRING, ENUM -> { + if (ShapeUtil.isEnum(target)) { + writer.write("var s string"); + writer.write("if err := d.ReadString($L, &s); err != nil { return err }", schemaName); + writer.write("v.Value = $T(s)", symbolProvider.toSymbol(target)); + writer.write("return nil"); + } else { + writer.write("return d.ReadString($L, &v.Value)", schemaName); + } + } case BLOB -> writer.write("return d.ReadBlob($L, &v.Value)", schemaName); case TIMESTAMP -> writer.write("return d.ReadTime($L, &v.Value)", schemaName); - case ENUM -> { - writer.write("var s string"); - writer.write("if err := d.ReadString($L, &s); err != nil { return err }", schemaName); - writer.write("v.Value = $T(s)", symbolProvider.toSymbol(target)); - writer.write("return nil"); - } case INT_ENUM -> { writer.write("var i int32"); writer.write("if err := d.ReadInt32($L, &i); err != nil { return err }", schemaName); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java index 9d1315527..e21bcfeba 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java @@ -53,7 +53,8 @@ private void renderDense(GoWriter writer) { "shapeName", shape.getId().getName(), "symbol", ctx.symbolProvider().toSymbol(shape), "memberSymbol", switch (member.getType()) { - case ENUM -> GoUniverseTypes.String; + case STRING, ENUM -> ShapeUtil.isEnum(member) + ? GoUniverseTypes.String : ctx.symbolProvider().toSymbol(member); case INT_ENUM -> GoUniverseTypes.Int32; case DOCUMENT -> SmithyGoDependency.SMITHY_DOCUMENT.valueSymbol("Value"); default -> ctx.symbolProvider().toSymbol(member); @@ -87,7 +88,8 @@ private void renderSparse(GoWriter writer) { "shapeName", shape.getId().getName(), "symbol", ctx.symbolProvider().toSymbol(shape), "memberSymbol", switch (member.getType()) { - case ENUM -> GoUniverseTypes.String; + case STRING, ENUM -> ShapeUtil.isEnum(member) + ? GoUniverseTypes.String : ctx.symbolProvider().toSymbol(member); case INT_ENUM -> GoUniverseTypes.Int32; case DOCUMENT -> SmithyGoDependency.SMITHY_DOCUMENT.valueSymbol("Value"); default -> ctx.symbolProvider().toSymbol(member); @@ -99,11 +101,13 @@ private void renderSparse(GoWriter writer) { private Writable renderSparseCast() { return switch (member.getType()) { - case ENUM, INT_ENUM -> goTemplate(""" + case STRING, ENUM, INT_ENUM -> ShapeUtil.isEnum(member) + ? goTemplate(""" func() $T { ev := $T(vv) return &ev - }()""", ctx.symbolProvider().toSymbol(member)); + }()""", ctx.symbolProvider().toSymbol(member)) + : goTemplate("&vv"); // don't need the address-of case BLOB, LIST, SET, MAP, UNION -> @@ -117,7 +121,9 @@ private Writable renderSparseCast() { private Writable renderDenseCast() { return switch (member.getType()) { - case ENUM, INT_ENUM -> goTemplate("$T(vv)", ctx.symbolProvider().toSymbol(member)); + case STRING, ENUM, INT_ENUM -> ShapeUtil.isEnum(member) + ? goTemplate("$T(vv)", ctx.symbolProvider().toSymbol(member)) + : goTemplate("vv"); case DOCUMENT -> renderDocumentCast(); default -> goTemplate("vv"); }; diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java index fe3a04af1..5d9a65568 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListSerializer.java @@ -82,10 +82,10 @@ private Writable renderSparseSerializeValue() { case DOUBLE -> wrapNilCheck(goTemplate("s.WriteFloat64(schema.ListMember(), *vv)")); - case STRING -> - wrapNilCheck(goTemplate("s.WriteString(schema.ListMember(), *vv)")); - case ENUM -> - wrapNilCheck(goTemplate("s.WriteString(schema.ListMember(), string(*vv))")); + case STRING, ENUM -> + ShapeUtil.isEnum(member) + ? wrapNilCheck(goTemplate("s.WriteString(schema.ListMember(), string(*vv))")) + : wrapNilCheck(goTemplate("s.WriteString(schema.ListMember(), *vv)")); case BOOLEAN -> wrapNilCheck(goTemplate("s.WriteBool(schema.ListMember(), *vv)")); case TIMESTAMP -> diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java index a383eb4c9..e32d839c9 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java @@ -54,7 +54,8 @@ private void renderDense(GoWriter writer) { "shapeName", shape.getId().getName(), "symbol", ctx.symbolProvider().toSymbol(shape), "valueSymbol", switch (value.getType()) { - case ENUM -> GoUniverseTypes.String; + case STRING, ENUM -> ShapeUtil.isEnum(value) + ? GoUniverseTypes.String : ctx.symbolProvider().toSymbol(value); case INT_ENUM -> GoUniverseTypes.Int32; case DOCUMENT -> SmithyGoDependency.SMITHY_DOCUMENT.valueSymbol("Value"); default -> ctx.symbolProvider().toSymbol(value); @@ -89,7 +90,8 @@ private void renderSparse(GoWriter writer) { "shapeName", shape.getId().getName(), "symbol", ctx.symbolProvider().toSymbol(shape), "valueSymbol", switch (value.getType()) { - case ENUM -> GoUniverseTypes.String; + case STRING, ENUM -> ShapeUtil.isEnum(value) + ? GoUniverseTypes.String : ctx.symbolProvider().toSymbol(value); case INT_ENUM -> GoUniverseTypes.Int32; case DOCUMENT -> SmithyGoDependency.SMITHY_DOCUMENT.valueSymbol("Value"); default -> ctx.symbolProvider().toSymbol(value); @@ -101,11 +103,13 @@ private void renderSparse(GoWriter writer) { private Writable renderSparseCast() { return switch (value.getType()) { - case ENUM, INT_ENUM -> goTemplate(""" + case STRING, ENUM, INT_ENUM -> ShapeUtil.isEnum(value) + ? goTemplate(""" func() $T { ev := $T(vv) return &ev - }()""", ctx.symbolProvider().toSymbol(value)); + }()""", ctx.symbolProvider().toSymbol(value)) + : goTemplate("&vv"); // don't need the address-of case BLOB, LIST, SET, MAP, UNION -> @@ -119,7 +123,9 @@ private Writable renderSparseCast() { private Writable renderDenseCast() { return switch (value.getType()) { - case ENUM, INT_ENUM -> goTemplate("$T(vv)", ctx.symbolProvider().toSymbol(value)); + case STRING, ENUM, INT_ENUM -> ShapeUtil.isEnum(value) + ? goTemplate("$T(vv)", ctx.symbolProvider().toSymbol(value)) + : goTemplate("vv"); case DOCUMENT -> renderDocumentCast(); default -> goTemplate("vv"); }; diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java index 21d05df8c..0c01d4238 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapSerializer.java @@ -83,10 +83,10 @@ private Writable renderSparseSerializeValue() { case DOUBLE -> wrapNilCheck(goTemplate("s.WriteFloat64(schema.MapValue(), *vv)")); - case STRING -> - wrapNilCheck(goTemplate("s.WriteString(schema.MapValue(), *vv)")); - case ENUM -> - wrapNilCheck(goTemplate("s.WriteString(schema.MapValue(), string(*vv))")); + case STRING, ENUM -> + ShapeUtil.isEnum(value) + ? wrapNilCheck(goTemplate("s.WriteString(schema.MapValue(), string(*vv))")) + : wrapNilCheck(goTemplate("s.WriteString(schema.MapValue(), *vv)")); case BOOLEAN -> wrapNilCheck(goTemplate("s.WriteBool(schema.MapValue(), *vv)")); case TIMESTAMP -> diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2EventStreamMiddleware.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2EventStreamMiddleware.java index 9067d14fb..ff9cc9d9f 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2EventStreamMiddleware.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2EventStreamMiddleware.java @@ -132,7 +132,7 @@ public Writable getFuncBody() { private Writable writerSetup(UnionShape inputEventStream) { var service = ctx.service(); - var schemaName = "schemas." + SchemaGenerator.getSchemaName(inputEventStream, service); + var schemaName = SchemaGenerator.getSchemaRef(inputEventStream, service); var adapterName = EventStreamGenerator.getEventStreamWriterAdapterName(service, inputEventStream); return goTemplate(""" @@ -178,7 +178,7 @@ defer func() { private Writable readerSetup(UnionShape outputEventStream) { var service = ctx.service(); - var schemaName = "schemas." + SchemaGenerator.getSchemaName(outputEventStream, service); + var schemaName = SchemaGenerator.getSchemaRef(outputEventStream, service); var adapterConstructor = EventStreamGenerator .getEventStreamReaderAdapterConstructor(service, outputEventStream); @@ -202,7 +202,7 @@ defer func() { private Writable initialRequest() { var inputShape = ctx.model().expectShape(operation.getInputShape()); - var inputSchemaName = "schemas." + SchemaGenerator.getSchemaName(inputShape, ctx.service()); + var inputSchemaName = SchemaGenerator.getSchemaRef(inputShape, ctx.service()); return goTemplate(""" if m.options.Protocol.HasInitialEventMessage() { @@ -223,7 +223,7 @@ private Writable initialRequest() { } private Writable initialResponse(software.amazon.smithy.model.shapes.Shape outputShape) { - var outputSchemaName = "schemas." + SchemaGenerator.getSchemaName(outputShape, ctx.service()); + var outputSchemaName = SchemaGenerator.getSchemaRef(outputShape, ctx.service()); return goTemplate(""" if m.options.Protocol.HasInitialEventMessage() { @@ -338,7 +338,7 @@ private Writable v2FuncBody( private Writable v2ReaderSetup(UnionShape outputEventStream) { var service = ctx.service(); - var schemaName = "schemas." + SchemaGenerator.getSchemaName(outputEventStream, service); + var schemaName = SchemaGenerator.getSchemaRef(outputEventStream, service); var adapterConstructor = EventStreamGenerator .getEventStreamReaderAdapterConstructor(service, outputEventStream); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java index f734627b2..6c250f214 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java @@ -9,6 +9,7 @@ import software.amazon.smithy.go.codegen.UnsupportedShapeException; import software.amazon.smithy.go.codegen.Writable; import software.amazon.smithy.go.codegen.knowledge.GoPointableIndex; +import software.amazon.smithy.go.codegen.util.ShapeUtil; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.StructureShape; @@ -38,10 +39,10 @@ public void accept(GoWriter writer) { .sorted(Comparator.comparing(MemberShape::getMemberName)) .toList(); writer.openBlock("func (v *$L) Deserialize(d smithy.ShapeDeserializer) error {", "}", symbol.getName(), () -> { - writer.openBlock("return smithy.ReadStruct(d, schemas.$L, func(s *smithy.Schema) error {", "})", SchemaGenerator.getSchemaName(shape, ctx.service()), () -> { + writer.openBlock("return smithy.ReadStruct(d, $L, func(s *smithy.Schema) error {", "})", SchemaGenerator.getSchemaRef(shape, ctx.service()), () -> { writer.openBlock("switch s {", "}", () -> { for (var member : members) { - writer.write("case schemas.$L:", SchemaGenerator.getMemberSchemaName(shape, member, ctx.service())); + writer.write("case $L:", SchemaGenerator.getMemberSchemaRef(shape, member, ctx.service())); renderMember(writer, member, ctx.model().expectShape(member.getTarget()), "v." + ctx.symbolProvider().toMemberName(member)); } }); @@ -51,50 +52,53 @@ public void accept(GoWriter writer) { } private void renderMember(GoWriter writer, MemberShape member, Shape target, String ident) { - var schemaName = SchemaGenerator.getMemberSchemaName(shape, member, ctx.service()); + var schemaName = SchemaGenerator.getMemberSchemaRef(shape, member, ctx.service()); var ptrSuffix = nilIndex.isNillable(member) ? "Ptr" : ""; switch (target.getType()) { case BYTE -> - writer.write("return d.ReadInt8$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + writer.write("return d.ReadInt8$L($L, &$L)", ptrSuffix, schemaName, ident); case SHORT -> - writer.write("return d.ReadInt16$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + writer.write("return d.ReadInt16$L($L, &$L)", ptrSuffix, schemaName, ident); case INTEGER -> - writer.write("return d.ReadInt32$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + writer.write("return d.ReadInt32$L($L, &$L)", ptrSuffix, schemaName, ident); case LONG -> - writer.write("return d.ReadInt64$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + writer.write("return d.ReadInt64$L($L, &$L)", ptrSuffix, schemaName, ident); case FLOAT -> - writer.write("return d.ReadFloat32$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + writer.write("return d.ReadFloat32$L($L, &$L)", ptrSuffix, schemaName, ident); case DOUBLE -> - writer.write("return d.ReadFloat64$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + writer.write("return d.ReadFloat64$L($L, &$L)", ptrSuffix, schemaName, ident); - case STRING -> - writer.write("return d.ReadString$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); - case ENUM -> - writer.write(""" - var ev string - if err := d.ReadString(schemas.$1L, &ev); err != nil { - return err - } - $2L = $3T(ev) - return nil""", schemaName, ident, ctx.symbolProvider().toSymbol(target)); + case STRING, ENUM -> { + if (ShapeUtil.isEnum(target)) { + writer.write(""" + var ev string + if err := d.ReadString($1L, &ev); err != nil { + return err + } + $2L = $3T(ev) + return nil""", schemaName, ident, ctx.symbolProvider().toSymbol(target)); + } else { + writer.write("return d.ReadString$L($L, &$L)", ptrSuffix, schemaName, ident); + } + } case INT_ENUM -> writer.write(""" var ev int32 - if err := d.ReadInt32(schemas.$1L, &ev); err != nil { + if err := d.ReadInt32($1L, &ev); err != nil { return err } $2L = $3T(ev) return nil""", schemaName, ident, ctx.symbolProvider().toSymbol(target)); case BOOLEAN -> - writer.write("return d.ReadBool$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + writer.write("return d.ReadBool$L($L, &$L)", ptrSuffix, schemaName, ident); case TIMESTAMP -> - writer.write("return d.ReadTime$L(schemas.$L, &$L)", ptrSuffix, schemaName, ident); + writer.write("return d.ReadTime$L($L, &$L)", ptrSuffix, schemaName, ident); case BLOB -> - writer.write("return d.ReadBlob(schemas.$L, &$L)", schemaName, ident); + writer.write("return d.ReadBlob($L, &$L)", schemaName, ident); case LIST, SET, MAP, UNION -> - writer.write("return deserialize$L(d, schemas.$L, &$L)", target.getId().getName(), schemaName, ident); + writer.write("return deserialize$L(d, $L, &$L)", target.getId().getName(), schemaName, ident); case STRUCTURE -> { if (nilIndex.isNillable(member)) { writer.write(""" @@ -110,7 +114,7 @@ private void renderMember(GoWriter writer, MemberShape member, Shape target, Str writer.addUseImports(SmithyGoDependency.SMITHY_DOCUMENT); writer.write(""" var dv smithydocument.Value - if err := d.ReadDocument(schemas.$L, &dv); err != nil { + if err := d.ReadDocument($L, &dv); err != nil { return err } if ov, ok := dv.(smithydocument.Opaque); ok { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java index bd53b3639..645e4d5b0 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java @@ -40,7 +40,7 @@ public void accept(GoWriter writer) { .sorted(Comparator.comparing(MemberShape::getMemberName)) .toList(); writer.addUseImports(SmithyGoDependency.SMITHY); - var schemaRef = "schemas." + SchemaGenerator.getSchemaName(shape, ctx.service()); + var schemaRef = SchemaGenerator.getSchemaRef(shape, ctx.service()); writer.openBlock("func (v *$L) Serialize(s smithy.ShapeSerializer) {", "}", symbol.getName(), () -> { writer.write("s.WriteStruct($L)", schemaRef); writer.write("v.SerializeMembers(s)"); @@ -58,7 +58,7 @@ public void accept(GoWriter writer) { } private void generateSerializeMember(GoWriter writer, MemberShape member, Shape target, String ident) { - var schemaName = "schemas." + SchemaGenerator.getMemberSchemaName(shape, member, ctx.service()); + var schemaName = SchemaGenerator.getMemberSchemaRef(shape, member, ctx.service()); var ptrSuffix = nilIndex.isNillable(member) ? "Ptr" : ""; switch (target.getType()) { case BYTE -> @@ -75,10 +75,13 @@ private void generateSerializeMember(GoWriter writer, MemberShape member, Shape case DOUBLE -> writer.write("s.WriteFloat64$L($L, $L)", ptrSuffix, schemaName, ident); - case STRING -> - writer.write("s.WriteString$L($L, $L)", ptrSuffix, schemaName, ident); - case ENUM -> - writer.write("s.WriteString($L, string($L))", schemaName, ident); + case STRING, ENUM -> { + if (ShapeUtil.isEnum(target)) { + writer.write("s.WriteString($L, string($L))", schemaName, ident); + } else { + writer.write("s.WriteString$L($L, $L)", ptrSuffix, schemaName, ident); + } + } case INT_ENUM -> writer.write("s.WriteInt32($L, int32($L))", schemaName, ident); case BOOLEAN -> diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java index e85d12970..571067e3e 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionDeserializer.java @@ -52,13 +52,13 @@ private Writable renderCases(java.util.List members) { var unionSymbol = ctx.symbolProvider().toSymbol(shape); for (var member : members) { var memberName = ctx.symbolProvider().toMemberName(member); - var variantSchema = SchemaGenerator.getMemberSchemaName(shape, member, ctx.service()); + var variantSchema = SchemaGenerator.getMemberSchemaRef(shape, member, ctx.service()); var memberSymbol = unionSymbol.toBuilder() .name(memberName) .build(); - w.write("case schemas.$L:", variantSchema); + w.write("case $L:", variantSchema); w.indent(); w.write("vv := &$T{}", memberSymbol); w.write("*v = vv"); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionSerializer.java index c91889251..bed9329c4 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/UnionSerializer.java @@ -53,9 +53,9 @@ private Writable renderCases(List members) { .name(ctx.symbolProvider().toMemberName(member)) .namespace(ctx.settings().getModuleName() + "/types", ".") .build(); - var variantSchema = SchemaGenerator.getMemberSchemaName(shape, member, ctx.service()); + var variantSchema = SchemaGenerator.getMemberSchemaRef(shape, member, ctx.service()); w.write("case *$T:", variantSymbol); - w.write(" s.WriteUnion(schema, schemas.$L, vv)", variantSchema); + w.write(" s.WriteUnion(schema, $L, vv)", variantSchema); } }; } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java index ac4468090..03862a5a8 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/util/ShapeUtil.java @@ -29,6 +29,7 @@ import software.amazon.smithy.model.shapes.ShapeType; import software.amazon.smithy.model.shapes.StringShape; import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.traits.EnumTrait; import software.amazon.smithy.model.traits.UnitTypeTrait; public final class ShapeUtil { @@ -82,4 +83,10 @@ public static Shape expectMember(Model model, CollectionShape shape) { public static Shape expectMember(Model model, MapShape shape) { return model.expectShape(shape.getValue().getTarget()); } + + // Returns true for both IDL2 enum shapes and IDL1 string + @enum shapes. + public static boolean isEnum(Shape shape) { + return shape.getType() == ShapeType.ENUM + || (shape.getType() == ShapeType.STRING && shape.hasTrait(EnumTrait.class)); + } } diff --git a/prelude/prelude.go b/prelude/prelude.go new file mode 100644 index 000000000..8d3333a75 --- /dev/null +++ b/prelude/prelude.go @@ -0,0 +1,31 @@ +// Package prelude defines schemas for the Smithy prelude shapes. +package prelude + +import "github.com/aws/smithy-go" + +// All shapes in the prelude. +var ( + String = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "String"}, smithy.ShapeTypeString, 0) + Blob = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "Blob"}, smithy.ShapeTypeBlob, 0) + Boolean = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "Boolean"}, smithy.ShapeTypeBoolean, 0) + Byte = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "Byte"}, smithy.ShapeTypeByte, 0) + Short = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "Short"}, smithy.ShapeTypeShort, 0) + Integer = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "Integer"}, smithy.ShapeTypeInteger, 0) + Long = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "Long"}, smithy.ShapeTypeLong, 0) + Float = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "Float"}, smithy.ShapeTypeFloat, 0) + Double = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "Double"}, smithy.ShapeTypeDouble, 0) + Timestamp = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "Timestamp"}, smithy.ShapeTypeTimestamp, 0) + Document = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "Document"}, smithy.ShapeTypeDocument, 0) + Unit = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "Unit"}, smithy.ShapeTypeStructure, 0) + + PrimitiveBoolean = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "PrimitiveBoolean"}, smithy.ShapeTypeBoolean, 0) + PrimitiveByte = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "PrimitiveByte"}, smithy.ShapeTypeByte, 0) + PrimitiveShort = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "PrimitiveShort"}, smithy.ShapeTypeShort, 0) + PrimitiveInteger = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "PrimitiveInteger"}, smithy.ShapeTypeInteger, 0) + PrimitiveLong = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "PrimitiveLong"}, smithy.ShapeTypeLong, 0) + PrimitiveFloat = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "PrimitiveFloat"}, smithy.ShapeTypeFloat, 0) + PrimitiveDouble = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "PrimitiveDouble"}, smithy.ShapeTypeDouble, 0) + + BigInteger = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "BigInteger"}, smithy.ShapeTypeBigInteger, 0) + BigDecimal = smithy.NewSchema(smithy.ShapeID{Namespace: "smithy.api", Name: "BigDecimal"}, smithy.ShapeTypeBigDecimal, 0) +) From e86c946cc9519e0c61ca7aa38d13bccbac55ced4 Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Mon, 11 May 2026 13:11:44 -0400 Subject: [PATCH 13/38] implement restxml (#656) * support restxml * make the error parsing better --- .../internal/httpbinding/deserializer.go | 4 +- .../internal/httpbinding/serializer.go | 20 +- aws-protocols/internal/xml/codec.go | 27 ++ aws-protocols/internal/xml/element_name.go | 77 ++++ aws-protocols/internal/xml/error.go | 27 +- aws-protocols/internal/xml/extract.go | 71 +-- .../internal/xml/shape_deserializer.go | 162 +++++-- .../internal/xml/shape_serializer.go | 422 ++++++++++++++++++ aws-protocols/internal/xml/writer.go | 129 ++++++ aws-protocols/restxml/restxml.go | 183 ++++++++ .../smithy/go/codegen/SchemaGenerator.java | 19 +- .../smithy/go/codegen/ServiceGenerator.java | 17 + .../smithy/go/codegen/SmithyGoDependency.java | 1 + .../smithy/go/codegen/UnionGenerator.java | 1 + schema.go | 87 +++- testing/xml/xmlToStruct.go | 18 +- trait.go | 2 +- transport/http/protocol.go | 7 +- 18 files changed, 1155 insertions(+), 119 deletions(-) create mode 100644 aws-protocols/internal/xml/codec.go create mode 100644 aws-protocols/internal/xml/element_name.go create mode 100644 aws-protocols/internal/xml/shape_serializer.go create mode 100644 aws-protocols/internal/xml/writer.go create mode 100644 aws-protocols/restxml/restxml.go diff --git a/aws-protocols/internal/httpbinding/deserializer.go b/aws-protocols/internal/httpbinding/deserializer.go index c3f2d37fd..de4bf5e13 100644 --- a/aws-protocols/internal/httpbinding/deserializer.go +++ b/aws-protocols/internal/httpbinding/deserializer.go @@ -165,9 +165,7 @@ func (d *ShapeDeserializer) ReadStringPtr(s *smithy.Schema, v **string) error { if err := d.ReadString(s, &val); err != nil { return err } - if val != "" { - *v = &val - } + *v = &val return nil } diff --git a/aws-protocols/internal/httpbinding/serializer.go b/aws-protocols/internal/httpbinding/serializer.go index 6510af0be..6d94f77ab 100644 --- a/aws-protocols/internal/httpbinding/serializer.go +++ b/aws-protocols/internal/httpbinding/serializer.go @@ -24,11 +24,11 @@ import ( // serializer for whatever protocol is being used. // // ShapeSerializer adds some API surface on top of the normal -// smithy.ShapeSerializer. Specifically it adds [GetRequestBody] for handing -// REST-protocol payloads, since the actual source of the payload is going to -// vary on a per-operation basis and isn't known until the input's Serialize is -// called. The caller (so, the protocol implementation) should set the HTTP -// request body according to the return of GetRequestBody. +// smithy.ShapeSerializer. Specifically it adds [ShapeSerializer.Build] for +// handing REST-protocol payloads, since the actual source of the payload is +// going to vary on a per-operation basis and isn't known until the input's +// Serialize is called. The caller (so, the protocol implementation) should +// set the HTTP request body by calling Build after Serialize. type ShapeSerializer struct { request *smithyhttp.Request encoder *httpbinding.Encoder @@ -82,19 +82,15 @@ func NewShapeSerializer(op *smithy.Schema, req *smithyhttp.Request, in smithy.Sh }, nil } -// GetRequestBody resolves the serialized body after Serialize has been called. -// It encodes the httpbinding values into the request and returns the stream -// and content type for the body. +// Build encodes HTTP binding values into the request and sets the request +// body. The defaultContentType is used for the protocol body (e.g. +// "application/json") when no explicit payload is present. // // The body is resolved in the following priority: // 1. Streaming payload (input implements StreamingInput with non-nil stream) // 2. Raw payload bytes (blob/string member with @httpPayload) // 3. Serialized protocol body (e.g. JSON) // 4. Empty struct payload (struct member with @httpPayload, sends "{}") -// -// Build encodes HTTP binding values into the request and sets the request -// body. The defaultContentType is used for the protocol body (e.g. -// "application/json") when no explicit payload is present. func (s *ShapeSerializer) Build(in smithy.Serializable, defaultContentType string) error { req := s.request diff --git a/aws-protocols/internal/xml/codec.go b/aws-protocols/internal/xml/codec.go new file mode 100644 index 000000000..e2e6371f5 --- /dev/null +++ b/aws-protocols/internal/xml/codec.go @@ -0,0 +1,27 @@ +package xml + +import ( + "github.com/aws/smithy-go" +) + +// Codec implements [smithy.Codec] for XML. +type Codec struct { + opts []func(*ShapeSerializerOptions) +} + +// NewCodec returns an XML codec with the given serializer options. +func NewCodec(opts ...func(*ShapeSerializerOptions)) *Codec { + return &Codec{opts: opts} +} + +var _ smithy.Codec = (*Codec)(nil) + +// Serializer returns an XML shape serializer. +func (c *Codec) Serializer() smithy.ShapeSerializer { + return NewShapeSerializer(c.opts...) +} + +// Deserializer returns an XML shape deserializer. +func (c *Codec) Deserializer(p []byte) smithy.ShapeDeserializer { + return NewShapeDeserializer(p) +} diff --git a/aws-protocols/internal/xml/element_name.go b/aws-protocols/internal/xml/element_name.go new file mode 100644 index 000000000..f18dd6311 --- /dev/null +++ b/aws-protocols/internal/xml/element_name.go @@ -0,0 +1,77 @@ +package xml + +import ( + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/traits" +) + +// Unlike json which has a sane recursive data model, the xml serializer at its +// core operates by writing ... except what ename is supposed to +// be depends on a handful of different factors based on serialization context +// and traits. The helpers here attempt to solve that in the most +// straightforward way possible. + +// base resolver for element names +func (s *ShapeSerializer) ename(schema *smithy.Schema) string { + // DIRECT only, if a member does not have an @xmlName but its target does, + // we don't use the one on the target + if t, ok := smithy.SchemaDirectTrait[*traits.XMLName](schema); ok { + return t.Name + } else if len(s.stack) == 0 { // the root + return schema.ID().Name + } + + return schema.MemberName() +} + +// wraps ename to check for the context of "am i in a list or map" +func (s *ShapeSerializer) ctxEname(schema *smithy.Schema) string { + if top := s.top(); top != nil { + switch top.kind { + case ctxKindList: + return top.itemName + case ctxKindMap: + if top.inMapEntry { + return s.ename(top.schema.MapValue()) + } + } + } + + return s.ename(schema) +} + +// structs with @httpPayload are special, preferring in order: +// - member/target @xmlName +// - target shape name +// +// otherwise just use context-based name +func (s *ShapeSerializer) structEname(schema *smithy.Schema) string { + if isPayload(schema) { + if t, ok := smithy.SchemaTrait[*traits.XMLName](schema); ok { + return t.Name + } + return schema.TargetID().Name + } + + return s.ctxEname(schema) +} + +// resolution for @xmlNamespace, which is generally sane +func (s *ShapeSerializer) xmlns(schema *smithy.Schema) *traits.XMLNamespace { + if top := s.top(); top != nil { + switch { + case top.flat && (top.kind == ctxKindList || top.kind == ctxKindMap): + // flattened lists/maps have no wrapper, inherit the + // collection's namespace if the member doesn't have its own + if _, ok := smithy.SchemaDirectTrait[*traits.XMLNamespace](schema); !ok { + ns, _ := smithy.SchemaDirectTrait[*traits.XMLNamespace](top.schema) + return ns + } + case top.kind == ctxKindMap && !top.inMapEntry: + return nil + } + } + + ns, _ := smithy.SchemaDirectTrait[*traits.XMLNamespace](schema) + return ns +} diff --git a/aws-protocols/internal/xml/error.go b/aws-protocols/internal/xml/error.go index 829b05977..366cf491d 100644 --- a/aws-protocols/internal/xml/error.go +++ b/aws-protocols/internal/xml/error.go @@ -1,6 +1,11 @@ package xml -import "io" +import "encoding/xml" + +type protocolError struct { + Code string `xml:"Code"` + Message string `xml:"Message"` +} // GetProtocolErrorInfo extracts Smithy error details from a query-protocol // response. @@ -17,25 +22,17 @@ func GetProtocolErrorInfo(payload []byte) (code, message string, errorBody []byt return } - // we are in a fragment here so just leverage ExtractElement to get the - // inner XML of these, instead of having to wrap in a tag to pass to the - // stdlib decoder - c, err := ExtractElement(errorBody, "Code") - if err != nil && err != io.EOF { - return - } - m, err := ExtractElement(errorBody, "Message") - if err != nil && err != io.EOF { + var errinf protocolError + if err = xml.Unmarshal(errorBody, &errinf); err != nil { return } - // clear err if it was io.EOF err = nil - if len(c) > 0 { - code = string(c) + if len(errinf.Code) > 0 { + code = errinf.Code } - if len(m) > 0 { - message = string(m) + if len(errinf.Message) > 0 { + message = errinf.Message } return } diff --git a/aws-protocols/internal/xml/extract.go b/aws-protocols/internal/xml/extract.go index 1c8470072..6d6bed8cd 100644 --- a/aws-protocols/internal/xml/extract.go +++ b/aws-protocols/internal/xml/extract.go @@ -7,12 +7,14 @@ import ( ) // ExtractElement finds the first occurrence of the named element in the XML -// and returns its INNER content as raw bytes. +// and returns it as raw bytes, including the element's opening and closing +// tags. // // This is used to extract both success and error responses from the protocol // XML that wraps them. // -// e.g. for awsquery, with an operation output shape named XmlListsResult: +// For example for awsquery, with an operation output shape named +// XmlListsResult: // // // @@ -21,11 +23,13 @@ import ( // // // -// ExtractElement gives you "foo...". +// ExtractElement(payload, "XmlListsResult") yields: // -// ExtractElement will forward the io.EOF returned by the underlying decoder if -// the target element is not found, which the caller can index on if they're -// looking for an optional element. +// foo... +// +// ExtractElement will forward the io.EOF returned by the underlying decoder +// if the target element is not found, which the caller can index on if +// they're looking for an optional element. func ExtractElement(payload []byte, name string) ([]byte, error) { dec := xml.NewDecoder(bytes.NewReader(payload)) for { @@ -34,39 +38,40 @@ func ExtractElement(payload []byte, name string) ([]byte, error) { return nil, err } - if start, ok := tok.(xml.StartElement); ok { - if strings.EqualFold(start.Name.Local, name) { - return extract(dec) - } + start, ok := tok.(xml.StartElement) + if !ok || !strings.EqualFold(start.Name.Local, name) { + continue } - } -} -func extract(dec *xml.Decoder) ([]byte, error) { - var buf bytes.Buffer - enc := xml.NewEncoder(&buf) - depth := 1 - - for depth > 0 { - tok, err := dec.Token() - if err != nil { + var buf bytes.Buffer + enc := xml.NewEncoder(&buf) + if err := enc.EncodeToken(start); err != nil { + return nil, err + } + if err := enc.Flush(); err != nil { return nil, err } - switch t := tok.(type) { - case xml.StartElement: - depth++ - enc.EncodeToken(t) - case xml.EndElement: - depth-- - if depth > 0 { - enc.EncodeToken(t) + depth := 1 + for depth > 0 { + tok, err := dec.Token() + if err != nil { + return nil, err + } + if err := enc.EncodeToken(tok); err != nil { + return nil, err + } + switch tok.(type) { + case xml.StartElement: + depth++ + case xml.EndElement: + depth-- } - case xml.CharData: - enc.EncodeToken(t) } - } - enc.Flush() - return buf.Bytes(), nil + if err := enc.Flush(); err != nil { + return nil, err + } + return buf.Bytes(), nil + } } diff --git a/aws-protocols/internal/xml/shape_deserializer.go b/aws-protocols/internal/xml/shape_deserializer.go index aac38f1f7..deba3f446 100644 --- a/aws-protocols/internal/xml/shape_deserializer.go +++ b/aws-protocols/internal/xml/shape_deserializer.go @@ -5,7 +5,6 @@ import ( "encoding/base64" "encoding/xml" "fmt" - "io" "math" "strconv" "strings" @@ -30,6 +29,9 @@ type deserCtx struct { schema *smithy.Schema flattened bool + startElem xml.StartElement // cached to read @xmlAttributes + attrIndex int + // for flattened list/map, ReadStructMember consumes the start element // when it discovers the member, subsequent calls to ReadMapKey or // ReadListItem will do that instead @@ -42,9 +44,12 @@ type deserCtx struct { // ShapeDeserializer implements unmarshaling of XML into Smithy shapes. // -// ShapeDeserializer expects the **inner XML** of the protocol response that -// represents the operation output. For example, an awsquery response body -// looks like this: +// ShapeDeserializer consumes whole XML — the payload handed to +// NewShapeDeserializer must include the outermost tag that opens the shape +// being read. This is because the deserializer retains the outer +// StartElement so @xmlAttribute members can draw from its attributes. +// +// For example, an awsquery response body looks like this: // // <[OperationName]Response> // <[OperationName]Result> @@ -58,16 +63,30 @@ type deserCtx struct { // // // -// The deserializer must receive "..." to operate correctly. +// The deserializer must receive the Result element including its opening +// and closing tags — that is: +// +// <[OperationName]Result> +// ... +// ... +// ... +// +// +// The outer Response wrapper must not be included. type ShapeDeserializer struct { dec *xml.Decoder peeked xml.Token stack []deserCtx + + // most recent start element we saw in the token stream, when we go into a + // struct context we grab it so we can read any @xmlAttributes + currStart *xml.StartElement + currAttr *string } var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil) -// NewShapeDeseralizer returns a new ShapeDeserializer. +// NewShapeDeserializer returns a new ShapeDeserializer. func NewShapeDeserializer(p []byte) *ShapeDeserializer { return &ShapeDeserializer{ dec: xml.NewDecoder(bytes.NewReader(p)), @@ -93,7 +112,7 @@ func (d *ShapeDeserializer) top() *deserCtx { } func xmlMemberName(schema *smithy.Schema) string { - if t, ok := smithy.SchemaTrait[*traits.XMLName](schema); ok { + if t, ok := smithy.SchemaDirectTrait[*traits.XMLName](schema); ok { return t.Name } return schema.MemberName() @@ -115,7 +134,12 @@ func findMember(schema *smithy.Schema, elemName string) *smithy.Schema { // ReadStruct implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error { - d.push(deserCtx{kind: ctxKindStruct, schema: s}) + if err := d.ensureStart(); err != nil { + return err + } + + start := *d.currStart + d.push(deserCtx{kind: ctxKindStruct, schema: s, startElem: start}) return nil } @@ -126,16 +150,13 @@ func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { return nil, fmt.Errorf("ReadStructMember called without ReadStruct") } + if m := d.nextAttrMember(ctx); m != nil { + return m, nil + } + for { start, ok, err := d.nextStart() if err != nil { - // on the top-level struct we are guaranteed to get an EOF since - // we're operating on inner XML - if err == io.EOF && len(d.stack) == 1 { - d.pop() - return nil, nil - } - return nil, err } if !ok { @@ -151,10 +172,51 @@ func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { continue } + if _, isAttr := smithy.SchemaTrait[*traits.XMLAttribute](member); isAttr { + if err := d.skip(); err != nil { + return nil, err + } + continue + } + return member, nil } } +func (d *ShapeDeserializer) nextAttrMember(ctx *deserCtx) *smithy.Schema { + attrs := ctx.startElem.Attr + for ctx.attrIndex < len(attrs) { + a := attrs[ctx.attrIndex] + ctx.attrIndex++ + + member := findAttrMember(ctx.schema, a.Name.Local) + if member == nil { + continue + } + + val := a.Value + d.currAttr = &val + return member + } + return nil +} + +func findAttrMember(schema *smithy.Schema, elemName string) *smithy.Schema { + if schema == nil { + return nil + } + + for _, m := range schema.Members() { + if _, ok := smithy.SchemaTrait[*traits.XMLAttribute](m); !ok { + continue + } + if strings.EqualFold(stripPrefix(xmlMemberName(m)), elemName) { + return m + } + } + return nil +} + // ReadList implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { _, flattened := smithy.SchemaTrait[*traits.XMLFlattened](s) @@ -312,12 +374,12 @@ func (d *ShapeDeserializer) readEntry(ks, vs *smithy.Schema) (string, bool, erro ctx := d.top() kname := "key" - if xn, ok := smithy.SchemaTrait[*traits.XMLName](ks); ok { + if xn, ok := smithy.SchemaDirectTrait[*traits.XMLName](ks); ok { kname = xn.Name } vname := "value" - if xn, ok := smithy.SchemaTrait[*traits.XMLName](vs); ok { + if xn, ok := smithy.SchemaDirectTrait[*traits.XMLName](vs); ok { vname = xn.Name } @@ -552,23 +614,29 @@ func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { // ReadUnion implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) { - start, ok, err := d.nextStart() - if err != nil { + if err := d.ensureStart(); err != nil { return nil, err } - if !ok { - return nil, nil - } - member := findMember(s, start.Name.Local) - if member == nil { - if err := d.skip(); err != nil { + for { + start, ok, err := d.nextStart() + if err != nil { return nil, err } - return d.ReadUnion(s) - } + if !ok { + return nil, nil + } + + member := findMember(s, start.Name.Local) + if member == nil { + if err := d.skip(); err != nil { + return nil, err + } + continue + } - return member, nil + return member, nil + } } // ReadDocument is unimplemented for XML. @@ -590,10 +658,25 @@ func (d *ShapeDeserializer) token() (xml.Token, error) { return tok, nil } - return d.dec.Token() + tok, err := d.dec.Token() + if err != nil { + return nil, err + } + + if start, ok := tok.(xml.StartElement); ok { + d.currStart = &start + } + + return tok, nil } func (d *ShapeDeserializer) chardata() (string, error) { + if d.currAttr != nil { + v := *d.currAttr + d.currAttr = nil + return v, nil + } + // a single "inner XML" node can be multiple xml.CharData so we need to // accumulate them var buf strings.Builder @@ -658,6 +741,18 @@ func (d *ShapeDeserializer) nextStart() (xml.StartElement, bool, error) { } } +// make sure we have the start element when we go into a struct/union context +func (d *ShapeDeserializer) ensureStart() error { + if d.currStart == nil { + if _, ok, err := d.nextStart(); err != nil { + return err + } else if !ok { + return fmt.Errorf("expected start element") + } + } + return nil +} + func (d *ShapeDeserializer) readInt(bits int) (int64, error) { text, err := d.chardata() if err != nil { @@ -683,4 +778,11 @@ func (d *ShapeDeserializer) readFloat() (float64, error) { default: return strconv.ParseFloat(text, 64) } -} \ No newline at end of file +} + +func stripPrefix(name string) string { + if _, after, ok := strings.Cut(name, ":"); ok { + return after + } + return name +} diff --git a/aws-protocols/internal/xml/shape_serializer.go b/aws-protocols/internal/xml/shape_serializer.go new file mode 100644 index 000000000..76ad95e22 --- /dev/null +++ b/aws-protocols/internal/xml/shape_serializer.go @@ -0,0 +1,422 @@ +package xml + +import ( + "encoding/base64" + "math" + "math/big" + "strconv" + "time" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/document" + smithytime "github.com/aws/smithy-go/time" + "github.com/aws/smithy-go/traits" +) + +type serCtx struct { + kind ctxKind + schema *smithy.Schema + flat bool + + wrapperName string // open/close ename for struct and NON-flat list/map + itemName string // per-item element name for list items and map _entries_ + inMapEntry bool + + // we have to buffer inner xml for structs because they may have + // @xmlAttribute members written in any order + // + // @xmlNamespace is just resolved at flush time + w *writer + attrs []attr +} + +// ShapeSerializer implements marshaling of Smithy shapes to XML. +type ShapeSerializer struct { + w *writer + + // TODO(serde2): SerdeStack[T], obviously this primitive has been reduped a + // bunch at this point + stack []serCtx + + opts ShapeSerializerOptions +} + +// ShapeSerializerOptions configures ShapeSerializer. +type ShapeSerializerOptions struct { + RootNamespaceURI string + RootNamespacePrefix string +} + +var _ smithy.ShapeSerializer = (*ShapeSerializer)(nil) + +// NewShapeSerializer creates a new ShapeSerializer. +func NewShapeSerializer(opts ...func(*ShapeSerializerOptions)) *ShapeSerializer { + o := ShapeSerializerOptions{} + for _, fn := range opts { + fn(&o) + } + return &ShapeSerializer{ + w: newWriter(), + opts: o, + } +} + +// Bytes returns the serialized XML bytes. +func (s *ShapeSerializer) Bytes() []byte { + return s.w.Bytes() +} + +// WriteInt8 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { + s.writeScalar(schema, strconv.FormatInt(int64(v), 10)) +} + +// WriteInt16 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { + s.writeScalar(schema, strconv.FormatInt(int64(v), 10)) +} + +// WriteInt32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { + s.writeScalar(schema, strconv.FormatInt(int64(v), 10)) +} + +// WriteInt64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { + s.writeScalar(schema, strconv.FormatInt(v, 10)) +} + +// WriteInt8Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt8Ptr(schema *smithy.Schema, v *int8) { + if v != nil { + s.WriteInt8(schema, *v) + } +} + +// WriteInt16Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt16Ptr(schema *smithy.Schema, v *int16) { + if v != nil { + s.WriteInt16(schema, *v) + } +} + +// WriteInt32Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt32Ptr(schema *smithy.Schema, v *int32) { + if v != nil { + s.WriteInt32(schema, *v) + } +} + +// WriteInt64Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteInt64Ptr(schema *smithy.Schema, v *int64) { + if v != nil { + s.WriteInt64(schema, *v) + } +} + +// WriteFloat32 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { + s.writeScalar(schema, formatFloat(float64(v), 32)) +} + +// WriteFloat64 implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { + s.writeScalar(schema, formatFloat(v, 64)) +} + +// WriteFloat32Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat32Ptr(schema *smithy.Schema, v *float32) { + if v != nil { + s.WriteFloat32(schema, *v) + } +} + +// WriteFloat64Ptr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteFloat64Ptr(schema *smithy.Schema, v *float64) { + if v != nil { + s.WriteFloat64(schema, *v) + } +} + +// WriteBool implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { + s.writeScalar(schema, strconv.FormatBool(v)) +} + +// WriteBoolPtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBoolPtr(schema *smithy.Schema, v *bool) { + if v != nil { + s.WriteBool(schema, *v) + } +} + +// WriteString implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { + s.writeScalar(schema, v) +} + +// WriteStringPtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStringPtr(schema *smithy.Schema, v *string) { + if v == nil { + return + } + s.WriteString(schema, *v) +} + +// WriteBigInteger is unimplemented. +func (s *ShapeSerializer) WriteBigInteger(_ *smithy.Schema, _ big.Int) { + panic("BigInteger not supported") +} + +// WriteBigDecimal is unimplemented. +func (s *ShapeSerializer) WriteBigDecimal(_ *smithy.Schema, _ big.Float) { + panic("BigDecimal not supported") +} + +// WriteBlob implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { + if v == nil { + return + } + s.writeScalar(schema, base64.StdEncoding.EncodeToString(v)) +} + +// WriteTime implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { + format := "date-time" + if t, ok := smithy.SchemaTrait[*traits.TimestampFormat](schema); ok { + format = t.Format + } + + switch format { + case "http-date": + s.writeScalar(schema, smithytime.FormatHTTPDate(v)) + case "epoch-seconds": + s.writeScalar(schema, strconv.FormatFloat(smithytime.FormatEpochSeconds(v), 'f', -1, 64)) + default: + s.writeScalar(schema, smithytime.FormatDateTime(v)) + } +} + +// WriteTimePtr implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { + if v != nil { + s.WriteTime(schema, *v) + } +} + +// WriteStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema) { + name := s.structEname(schema) + + // @xmlNamespace on a target structure does NOT propagate through member + // references (the spec says nothing about inheritance, and the protocol + // tests confirm: XmlNamespaceNested has @xmlNamespace but the + // element does not get it). The exception is @httpPayload, where the + // member and its target represent the same XML element. + + ctx := serCtx{ + kind: ctxKindStruct, + wrapperName: name, + schema: schema, + w: s.w, + } + s.w = newWriter() + s.push(ctx) +} + +// CloseStruct implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseStruct() { + ctx := s.pop() + + var ns *traits.XMLNamespace + if isPayload(ctx.schema) { + ns, _ = smithy.SchemaTrait[*traits.XMLNamespace](ctx.schema) + } else { + ns, _ = smithy.SchemaDirectTrait[*traits.XMLNamespace](ctx.schema) + } + + // special case for the root struct where the service set a namespace + if ns == nil && len(s.stack) == 0 && s.opts.RootNamespaceURI != "" { + ns = &traits.XMLNamespace{ + URI: s.opts.RootNamespaceURI, + Prefix: s.opts.RootNamespacePrefix, + } + } + + inner := s.w + s.w = ctx.w + s.w.writeStart(ctx.wrapperName, ns, ctx.attrs) + s.w.writeInner(inner) + s.w.writeEnd(ctx.wrapperName) +} + +// WriteUnion implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema, v smithy.Serializable) { + name := s.ctxEname(schema) + s.w.writeStart(name, s.xmlns(schema), nil) + v.Serialize(s) + s.w.writeEnd(name) +} + +// WriteNil implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteNil(schema *smithy.Schema) { +} + +// WriteList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { + _, flat := smithy.SchemaTrait[*traits.XMLFlattened](schema) + + ename := s.ename(schema) + iname := ename + if !flat { + ns, _ := smithy.SchemaDirectTrait[*traits.XMLNamespace](schema) + s.w.writeStart(ename, ns, nil) + iname = s.ename(schema.ListMember()) + } else { + ename = "" + } + + s.push(serCtx{ + kind: ctxKindList, + wrapperName: ename, + itemName: iname, + schema: schema, + flat: flat, + }) +} + +// CloseList implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseList() { + ctx := s.pop() + if !ctx.flat { + s.w.writeEnd(ctx.wrapperName) + } +} + +// WriteMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { + _, flattened := smithy.SchemaTrait[*traits.XMLFlattened](schema) + + wrapperName := s.ename(schema) + itemName := "entry" + if flattened { + itemName = wrapperName + wrapperName = "" + } else { + ns, _ := smithy.SchemaDirectTrait[*traits.XMLNamespace](schema) + s.w.writeStart(wrapperName, ns, nil) + } + + s.push(serCtx{ + kind: ctxKindMap, + wrapperName: wrapperName, + itemName: itemName, + schema: schema, + flat: flattened, + }) +} + +// WriteKey implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteKey(schema *smithy.Schema, k string) { + top := s.top() + if top == nil || top.kind != ctxKindMap { + return + } + + if top.inMapEntry { + s.w.writeEnd(top.itemName) + top.inMapEntry = false + } + + s.w.writeStart(top.itemName, nil, nil) + + keyName := s.ename(schema) + ns, _ := smithy.SchemaDirectTrait[*traits.XMLNamespace](top.schema.MapKey()) + s.w.writeStart(keyName, ns, nil) + s.w.writeChardata(k) + s.w.writeEnd(keyName) + + top.inMapEntry = true +} + +// CloseMap implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseMap() { + ctx := s.pop() + + if ctx.inMapEntry { + s.w.writeEnd(ctx.itemName) + } + + if ctx.wrapperName != "" { + s.w.writeEnd(ctx.wrapperName) + } +} + +// WriteDocument is unimplemented for XML. +func (s *ShapeSerializer) WriteDocument(schema *smithy.Schema, v document.Value) { + panic("WriteDocument not supported for XML") +} + +func (s *ShapeSerializer) push(ctx serCtx) { + s.stack = append(s.stack, ctx) +} + +func (s *ShapeSerializer) pop() serCtx { + n := len(s.stack) + ctx := s.stack[n-1] + s.stack = s.stack[:n-1] + return ctx +} + +func (s *ShapeSerializer) top() *serCtx { + if len(s.stack) == 0 { + return nil + } + return &s.stack[len(s.stack)-1] +} + +func (s *ShapeSerializer) writeScalar(schema *smithy.Schema, v string) { + if s.bufferAttribute(schema, v) { + return + } + + name := s.ctxEname(schema) + s.w.writeStart(name, s.xmlns(schema), nil) + s.w.writeChardata(v) + s.w.writeEnd(name) +} + +func (s *ShapeSerializer) bufferAttribute(schema *smithy.Schema, v string) bool { + if _, ok := smithy.SchemaTrait[*traits.XMLAttribute](schema); !ok { + return false + } + + top := s.top() + if top == nil || top.kind != ctxKindStruct { + return false + } + + top.attrs = append(top.attrs, attr{ + name: s.ename(schema), + value: v, + }) + return true +} + +func formatFloat(v float64, bits int) string { + switch { + case math.IsNaN(v): + return "NaN" + case math.IsInf(v, 1): + return "Infinity" + case math.IsInf(v, -1): + return "-Infinity" + } + return strconv.FormatFloat(v, 'g', -1, bits) +} + +func isPayload(schema *smithy.Schema) bool { + _, ok := smithy.SchemaTrait[*traits.HTTPPayload](schema) + return ok +} diff --git a/aws-protocols/internal/xml/writer.go b/aws-protocols/internal/xml/writer.go new file mode 100644 index 000000000..aead0260b --- /dev/null +++ b/aws-protocols/internal/xml/writer.go @@ -0,0 +1,129 @@ +package xml + +import ( + "bytes" + "unicode/utf8" + + "github.com/aws/smithy-go/traits" +) + +type writer struct { + buf *bytes.Buffer +} + +func newWriter() *writer { + return &writer{ + buf: bytes.NewBuffer(nil), + } +} + +type attr struct { + name, value string +} + +func (w *writer) writeChardata(s string) { + escapeString(w.buf, s) +} + +func (w *writer) writeStart(name string, ns *traits.XMLNamespace, attrs []attr) { + w.buf.WriteByte('<') + escapeString(w.buf, name) + if ns != nil { + w.buf.WriteString(" xmlns") + if ns.Prefix != "" { + w.buf.WriteByte(':') + escapeString(w.buf, ns.Prefix) + } + w.buf.WriteString(`="`) + escapeString(w.buf, ns.URI) + w.buf.WriteByte('"') + } + for _, a := range attrs { + w.buf.WriteByte(' ') + escapeString(w.buf, a.name) + w.buf.WriteString(`="`) + escapeString(w.buf, a.value) + w.buf.WriteByte('"') + } + w.buf.WriteByte('>') +} + +func (w *writer) writeEnd(name string) { + w.buf.WriteString("') +} + +func (w *writer) writeInner(inner *writer) { + w.buf.Write(inner.buf.Bytes()) +} + +func (w *writer) Bytes() []byte { + return w.buf.Bytes() +} + +// copied from smithy-go/encoding/xml/escape.go + +var ( + escQuot = []byte(""") + escApos = []byte("'") + escAmp = []byte("&") + escLT = []byte("<") + escGT = []byte(">") + escTab = []byte(" ") + escNL = []byte(" ") + escCR = []byte(" ") + escFFFD = []byte("\uFFFD") + escNextLine = []byte("…") + escLS = []byte("
") +) + +func isInCharacterRange(r rune) bool { + return r == 0x09 || + r == 0x0A || + r == 0x0D || + r >= 0x20 && r <= 0xD7FF || + r >= 0xE000 && r <= 0xFFFD || + r >= 0x10000 && r <= 0x10FFFF +} + +func escapeString(w *bytes.Buffer, s string) { + var esc []byte + last := 0 + for i := 0; i < len(s); { + r, width := utf8.DecodeRuneInString(s[i:]) + i += width + switch r { + case '"': + esc = escQuot + case '\'': + esc = escApos + case '&': + esc = escAmp + case '<': + esc = escLT + case '>': + esc = escGT + case '\t': + esc = escTab + case '\n': + esc = escNL + case '\r': + esc = escCR + case '\u0085': + esc = escNextLine + case '\u2028': + esc = escLS + default: + if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) { + esc = escFFFD + break + } + continue + } + w.WriteString(s[last : i-width]) + w.Write(esc) + last = i + } + w.WriteString(s[last:]) +} diff --git a/aws-protocols/restxml/restxml.go b/aws-protocols/restxml/restxml.go new file mode 100644 index 000000000..596ebb558 --- /dev/null +++ b/aws-protocols/restxml/restxml.go @@ -0,0 +1,183 @@ +package restxml + +import ( + "bytes" + "context" + "fmt" + "io" + + "github.com/aws/smithy-go" + internalhttpbinding "github.com/aws/smithy-go/aws-protocols/internal/httpbinding" + internalxml "github.com/aws/smithy-go/aws-protocols/internal/xml" + "github.com/aws/smithy-go/eventstream" + smithyhttp "github.com/aws/smithy-go/transport/http" +) + +// Protocol implements aws.protocols#restXml. +type Protocol struct { + *eventstream.Codec + + seropts func(*internalxml.ShapeSerializerOptions) +} + +var _ smithyhttp.ClientProtocol = (*Protocol)(nil) + +// New returns an instance of the aws.protocols#restXml protocol. +func New() *Protocol { + return &Protocol{ + Codec: &eventstream.Codec{ + Codec: internalxml.NewCodec(), + ContentType: "application/xml", + }, + } +} + +// NewWithNamespace returns an instance of the aws.protocols#restXml protocol +// configured with the given XML namespace URI (and optional prefix) to emit +// as an xmlns attribute on the request body's root element. Corresponds to +// the service-level @xmlNamespace trait. +func NewWithNamespace(uri, prefix string) *Protocol { + xmlOpts := func(o *internalxml.ShapeSerializerOptions) { + o.RootNamespaceURI = uri + o.RootNamespacePrefix = prefix + } + return &Protocol{ + Codec: &eventstream.Codec{ + Codec: internalxml.NewCodec(xmlOpts), + ContentType: "application/xml", + }, + seropts: xmlOpts, + } +} + +// ID identifies the protocol. +func (*Protocol) ID() string { + return "aws.protocols#restXml" +} + +// HasInitialEventMessage implements [smithyhttp.ClientProtocol]. +func (*Protocol) HasInitialEventMessage() bool { + return false +} + +// SerializeRequest serializes a request for restxml. +func (p *Protocol) SerializeRequest( + ctx context.Context, + op *smithy.OperationSchema, + in smithy.Serializable, + req *smithyhttp.Request, +) error { + serializer, err := internalhttpbinding.NewShapeSerializer(op.Schema, req, p.newSerializer()) + if err != nil { + return err + } + + in.Serialize(serializer) + + contentType := "application/xml" + if op.IsInputEventStream() { + contentType = "application/vnd.amazon.eventstream" + } + if err := serializer.Build(in, contentType); err != nil { + return fmt.Errorf("build request: %w", err) + } + + return nil +} + +// DeserializeResponse deserializes a response for restXml. +func (p *Protocol) DeserializeResponse( + ctx context.Context, + op *smithy.OperationSchema, + types *smithy.TypeRegistry, + resp *smithyhttp.Response, + out smithy.Deserializable, +) error { + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return p.deserializeError(types, resp) + } + + if so, ok := out.(smithy.StreamingOutput); ok { + so.SetPayloadStream(resp.Body) + deser := internalhttpbinding.NewShapeDeserializer(resp.Response, deser(nil, op.Output), nil) + if err := out.Deserialize(deser); err != nil { + return &smithy.DeserializationError{Err: err} + } + return nil + } + + if op.IsOutputEventStream() { + deser := internalhttpbinding.NewShapeDeserializer(resp.Response, deser(nil, op.Output), nil) + if err := out.Deserialize(deser); err != nil { + return &smithy.DeserializationError{Err: err} + } + return nil + } + + payload, err := io.ReadAll(resp.Body) + if err != nil { + return &smithy.DeserializationError{Err: err} + } + + deser := internalhttpbinding.NewShapeDeserializer(resp.Response, deser(payload, op.Output), payload) + if err := out.Deserialize(deser); err != nil { + return &smithy.DeserializationError{Err: err} + } + + return nil +} + +func (p *Protocol) newSerializer() *internalxml.ShapeSerializer { + if p.seropts != nil { + return internalxml.NewShapeSerializer(p.seropts) + } + return internalxml.NewShapeSerializer() +} + +func (p *Protocol) deserializeError(types *smithy.TypeRegistry, resp *smithyhttp.Response) error { + var buf bytes.Buffer + if _, err := io.Copy(&buf, resp.Body); err != nil { + return &smithy.DeserializationError{Err: fmt.Errorf("read error response body: %w", err)} + } + payload := buf.Bytes() + + errorCode, errorMessage, errorBody, err := internalxml.GetProtocolErrorInfo(payload) + if err != nil { + return &smithy.DeserializationError{Err: err} + } + + perr, ok := types.DeserializableError(errorCode) + if !ok { + return &smithy.GenericAPIError{ + Code: errorCode, + Message: errorMessage, + } + } + + if len(errorBody) > 0 { + deser := internalhttpbinding.NewShapeDeserializer(resp.Response, internalxml.NewShapeDeserializer(errorBody), payload) + if err := perr.Deserialize(deser); err != nil { + return &smithy.DeserializationError{Err: err} + } + } + + return perr +} + +// returns a ShapeDeserializer over the (possibly empty) payload, wrapped in +// a synthetic outer element whose name matches the operation output shape so +// the schema-serde XML deserializer can open it uniformly +func deser(payload []byte, out *smithy.Schema) smithy.ShapeDeserializer { + if len(payload) != 0 { + return internalxml.NewShapeDeserializer(payload) + } + + name := "Response" + if out != nil { + if n := out.ID().Name; n != "" { + name = n + } + } + payload = []byte("<" + name + ">") + return internalxml.NewShapeDeserializer(payload) +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java index 68fbbe12b..f67dd80dc 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java @@ -4,11 +4,13 @@ import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; import java.util.Map; +import java.util.Optional; import software.amazon.smithy.go.codegen.util.ShapeUtil; import software.amazon.smithy.model.loader.Prelude; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.Shape; +import software.amazon.smithy.model.shapes.ShapeId; import software.amazon.smithy.model.traits.Trait; import software.amazon.smithy.utils.SmithyInternalApi; import software.amazon.smithy.utils.StringUtils; @@ -47,6 +49,18 @@ public static String getMemberSchemaName(Shape shape, MemberShape member, Servic return String.format("%s_%s", getSchemaName(shape, service), member.getMemberName()); } + // modelShapeID returns the original model shape ID, preferring the + // Synthetic trait's archetype (if any) so that synthetic clones of Input / + // Output inline shapes carry the Smithy model's shape ID rather than the + // Go-codegen synthetic ID. + private static ShapeId modelShapeID(Shape shape) { + Optional synth = shape.getTrait(Synthetic.class); + if (synth.isPresent() && synth.get().getArchetype().isPresent()) { + return synth.get().getArchetype().get(); + } + return shape.getId(); + } + // Returns the schema reference for use outside the schemas package (e.g. in // serde generators). Prelude shapes are already package-qualified, local // shapes get the "schemas." prefix. @@ -70,6 +84,7 @@ public void accept(GoWriter writer) { return; } var service = ctx.service(); + var modelID = modelShapeID(shape); writer.addUseImports(SmithyGoDependency.SMITHY); writer.writeGoTemplate(""" var $ident:L = smithy.NewSchema(smithy.ShapeID{ @@ -80,8 +95,8 @@ public void accept(GoWriter writer) { """, Map.of( "ident", getSchemaName(shape, service), - "namespace", shape.getId().getNamespace(), - "name", shape.getId().getName(), + "namespace", modelID.getNamespace(), + "name", modelID.getName(), "type", StringUtils.capitalize(shape.getType().toString()), "numMembers", shape.members().size(), "traits", renderVariadicTraits(shape.getAllTraits().values()), diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java index bf083027e..f051a8446 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java @@ -34,6 +34,7 @@ import software.amazon.smithy.aws.traits.protocols.AwsQueryTrait; import software.amazon.smithy.aws.traits.protocols.Ec2QueryTrait; import software.amazon.smithy.aws.traits.protocols.RestJson1Trait; +import software.amazon.smithy.aws.traits.protocols.RestXmlTrait; import software.amazon.smithy.codegen.core.CodegenException; import software.amazon.smithy.codegen.core.Symbol; import software.amazon.smithy.codegen.core.SymbolProvider; @@ -55,6 +56,7 @@ import software.amazon.smithy.model.knowledge.TopDownIndex; import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.ShapeId; +import software.amazon.smithy.model.traits.XmlNamespaceTrait; import software.amazon.smithy.protocol.traits.Rpcv2CborTrait; import software.amazon.smithy.utils.MapUtils; @@ -298,11 +300,26 @@ private Writable resolveDefaultProtocol() { SmithyGoDependency.SMITHY_AWS_PROTOCOLS_EC2QUERY.func("New"), service.getVersion() ); + } else if (preferred.equals(RestXmlTrait.ID)) { + return restXmlNew(); } else { return goTemplate("nil"); } } + private Writable restXmlNew() { + var ns = service.getTrait(XmlNamespaceTrait.class); + if (ns.isEmpty()) { + return goTemplate("$T()", + SmithyGoDependency.SMITHY_AWS_PROTOCOLS_RESTXML.func("New")); + } + var t = ns.get(); + return goTemplate("$T($S, $S)", + SmithyGoDependency.SMITHY_AWS_PROTOCOLS_RESTXML.func("NewWithNamespace"), + t.getUri(), + t.getPrefix().orElse("")); + } + private Writable generateGetOptions() { var docs = autoDocTemplate(""" Options returns a copy of the client configuration. diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java index 0a1be7b1f..c18d78b95 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java @@ -88,6 +88,7 @@ public final class SmithyGoDependency { public static final GoDependency SMITHY_AWS_PROTOCOLS_RESTJSON1 = smithy("aws-protocols/restjson1"); public static final GoDependency SMITHY_AWS_PROTOCOLS_AWSQUERY = smithy("aws-protocols/awsquery"); public static final GoDependency SMITHY_AWS_PROTOCOLS_EC2QUERY = smithy("aws-protocols/ec2query"); + public static final GoDependency SMITHY_AWS_PROTOCOLS_RESTXML = smithy( "aws-protocols/restxml"); public static final GoDependency SMITHY_HTTP_PROTOCOLS = smithy("smithy-http-protocols"); public static final GoDependency SMITHY_HTTP_PROTOCOLS_RPCV2 = smithy("smithy-http-protocols/rpcv2"); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java index 192add0cf..b891bd819 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java @@ -138,6 +138,7 @@ private void generateMemberSerializer(GoWriter writer, MemberShape member, Strin case BIG_DECIMAL -> writer.write("s.WriteBigDecimal($L, v.Value)", schemaName); case STRUCTURE -> writer.write("s.WriteStruct($L)\nv.Value.SerializeMembers(s)\ns.CloseStruct()", schemaName); // struct variants are value types case LIST, SET, MAP -> writer.write("serialize$L(s, $L, v.Value)", target.getId().getName(), schemaName); + case UNION -> writer.write("serialize$L(s, $L, v.Value)", target.getId().getName(), schemaName); case DOCUMENT -> { writer.addUseImports(SmithyGoDependency.SMITHY_DOCUMENT); writer.write("s.WriteDocument($L, &smithydocument.Opaque{Value: v.Value})", schemaName); diff --git a/schema.go b/schema.go index 9e1fb3eb0..da3c2763a 100644 --- a/schema.go +++ b/schema.go @@ -64,11 +64,12 @@ func stoid(s string) ShapeID { // Generated clients use schemas at runtime to dynamically (de)serialize // request/responses. type Schema struct { - id ShapeID - typ ShapeType - members map[string]*Schema // member name -> schema - traits map[string]Trait // trait ID -> trait - targetID ShapeID // for member schemas, the target's shape ID + id ShapeID + typ ShapeType + members map[string]*Schema // member name -> schema + traits map[string]Trait // trait ID -> trait (effective view; for members, target's traits merged with member's overrides) + directTraits map[string]Trait // trait ID -> trait (member schemas only: only traits declared directly on the member) + targetID ShapeID // for member schemas, the target's shape ID listMember *Schema mapKey, mapValue *Schema @@ -91,23 +92,35 @@ func NewSchema(id ShapeID, typ ShapeType, numMembers int, traits ...Trait) *Sche // AddMember adds a member to the schema derived from the target, with // optional trait overrides. The member schema is returned for caller // reference. +// +// The member schema's effective trait view (accessed via [SchemaTrait]) +// inherits all of the target's traits, then applies the overrides. The +// member's direct trait view (accessed via [SchemaDirectTrait]) contains +// only the overrides, i.e. the traits declared directly on the member. func (s *Schema) AddMember(name string, target *Schema, traits ...Trait) *Schema { - m := &Schema{ - id: ShapeID{Member: name}, - typ: target.typ, - members: target.members, - traits: maps.Clone(target.traits), - targetID: target.id, - listMember: target.listMember, - mapKey: target.mapKey, - mapValue: target.mapValue, + directs := make(map[string]Trait, len(traits)) + for _, t := range traits { + directs[t.TraitID()] = t } - if len(m.traits) == 0 && len(traits) != 0 { - m.traits = map[string]Trait{} + merged := maps.Clone(target.traits) + if merged == nil { + merged = map[string]Trait{} } - for _, t := range traits { - m.traits[t.TraitID()] = t + for id, t := range directs { + merged[id] = t + } + + m := &Schema{ + id: ShapeID{Member: name}, + typ: target.typ, + members: target.members, + traits: merged, + directTraits: directs, + targetID: target.id, + listMember: target.listMember, + mapKey: target.mapKey, + mapValue: target.mapValue, } s.members[name] = m @@ -142,6 +155,11 @@ func (s *Schema) MemberName() string { return s.id.Member } +// ID returns the shape ID of the schema. +func (s *Schema) ID() ShapeID { + return s.id +} + // TargetID returns the shape ID of the member's target shape. func (s *Schema) TargetID() ShapeID { return s.targetID @@ -192,7 +210,11 @@ func (s *OperationSchema) IsOutputEventStream() bool { return s.outputStream } -// Trait returns the target trait on the schema if it exists. +// SchemaTrait returns the target trait on the schema if it exists. +// +// For member schemas this returns the effective trait, which is the trait +// declared directly on the member if present, else the trait inherited from +// the target shape. func SchemaTrait[T Trait](s *Schema) (T, bool) { var trait T @@ -209,6 +231,33 @@ func SchemaTrait[T Trait](s *Schema) (T, bool) { return tt, ok } +// SchemaDirectTrait returns the target trait on the schema if it was +// declared directly on the schema. +// +// For member schemas this returns the trait only if it was declared on the +// member itself, ignoring any trait inherited from the target shape. For +// non-member schemas this is equivalent to [SchemaTrait]. +func SchemaDirectTrait[T Trait](s *Schema) (T, bool) { + var trait T + + if s == nil { + return trait, false + } + + source := s.directTraits + if source == nil { + source = s.traits + } + + opaque, ok := source[trait.TraitID()] + if !ok { + return trait, false + } + + tt, ok := opaque.(T) + return tt, ok +} + func isEventStream(s *Schema) bool { for _, m := range s.members { if m.typ != ShapeTypeUnion { diff --git a/testing/xml/xmlToStruct.go b/testing/xml/xmlToStruct.go index 1bfc7ba98..7ea21ecf9 100644 --- a/testing/xml/xmlToStruct.go +++ b/testing/xml/xmlToStruct.go @@ -137,7 +137,7 @@ func StructToXML(e *xml.Encoder, node *XMLNode, sorted bool) error { // Sort Attributes attrs := node.Attr if sorted { - sortedAttrs := make([]xml.Attr, len(attrs)) + sortedAttrs := make([]xml.Attr, 0, len(attrs)) for _, k := range node.Attr { sortedAttrs = append(sortedAttrs, k) } @@ -145,7 +145,21 @@ func StructToXML(e *xml.Encoder, node *XMLNode, sorted bool) error { attrs = sortedAttrs } - st := xml.StartElement{Name: node.Name, Attr: attrs} + // Filter out default xmlns attributes that encoding/xml will regenerate + // from Name.Space. Without this, XML produced with literal xmlns + // attributes (rather than through encoding/xml's namespace-aware API) + // gets those declarations doubled on re-encode. Prefixed namespace + // declarations (xmlns:prefix="...") are preserved since the encoder + // does not regenerate those. + filtered := make([]xml.Attr, 0, len(attrs)) + for _, a := range attrs { + if a.Name.Space == "" && a.Name.Local == "xmlns" { + continue + } + filtered = append(filtered, a) + } + + st := xml.StartElement{Name: node.Name, Attr: filtered} e.EncodeToken(st) // return fmt.Errorf("encoder string : %s, %s, %s", node.Name.Local, node.Name.Space, st.Attr) diff --git a/trait.go b/trait.go index a70b10abc..a6e8ca157 100644 --- a/trait.go +++ b/trait.go @@ -4,7 +4,7 @@ package smithy // related to (de)serialization are included in code-generated Schemas for the // client. type Trait interface { - TraitID() string + TraitID() string // TODO(serde2): should return a ShapeID } // TODO(serde2): investigate performance tradeoff of using an "indexed" map for diff --git a/transport/http/protocol.go b/transport/http/protocol.go index db741c542..b54531f7d 100644 --- a/transport/http/protocol.go +++ b/transport/http/protocol.go @@ -7,6 +7,9 @@ import ( "github.com/aws/smithy-go" ) +// TODO(serde2): review API surface for all Protocol impls, their New()s, and +// the way they are configured (options vs codec options) + // ClientProtocol defines the interface through which client-side operation // request/responses are (de)serialized across the wire. // @@ -14,7 +17,7 @@ import ( // to do so. In practice, a generated client will utilize one of the predefined // protocols implemented as part of the Smithy client runtime. type ClientProtocol interface { - ID() string + ID() string // TODO(serde2): this should return shape ID SerializeRequest(context.Context, *smithy.OperationSchema, smithy.Serializable, *Request) error DeserializeResponse(ctx context.Context, schema *smithy.OperationSchema, types *smithy.TypeRegistry, resp *Response, out smithy.Deserializable) error @@ -24,4 +27,4 @@ type ClientProtocol interface { DeserializeEventMessage(schema *smithy.Schema, types *smithy.TypeRegistry, r io.Reader) (smithy.Deserializable, error) SerializeInitialRequest(schema *smithy.Schema, v smithy.Serializable, w io.Writer) error DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, out smithy.Deserializable) error -} \ No newline at end of file +} From 60bad24e39554fa1818e8776cc28d4bd4c946e0d Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Tue, 12 May 2026 14:11:24 -0400 Subject: [PATCH 14/38] serde2 lightning round (#659) * TODO lightning round * remove dead GetServiceName on ctx --- .../awsjson10.go => awsjson/awsjson.go} | 104 ++++++++++------ aws-protocols/awsquery/awsquery.go | 31 ++--- aws-protocols/ec2query/ec2query.go | 30 +++-- .../internal/httpbinding/deserializer.go | 40 ++++-- aws-protocols/internal/json/codec.go | 20 --- .../internal/json/shape_deserializer.go | 19 +-- .../internal/json/shape_serializer.go | 29 +++-- .../internal/query/shape_serializer.go | 60 ++++----- aws-protocols/internal/xml/codec.go | 27 ---- aws-protocols/internal/xml/element_name.go | 6 +- .../internal/xml/shape_deserializer.go | 56 +++------ .../internal/xml/shape_serializer.go | 46 +++---- aws-protocols/restjson1/restjson1.go | 33 ++--- aws-protocols/restxml/restxml.go | 55 ++++---- .../smithy/go/codegen/CodegenVisitor.java | 10 ++ .../go/codegen/DefaultTraitGenerators.java | 2 + .../smithy/go/codegen/ServiceGenerator.java | 57 +++------ .../smithy/go/codegen/SmithyGoDependency.java | 2 +- .../smithy/go/codegen/TypeRegistry.java | 8 +- .../codegen/serde2/StructureDeserializer.java | 4 + .../codegen/serde2/StructureSerializer.java | 4 + eventstream/types.go | 26 ++++ .../eventstream}/codec.go | 117 +++++++----------- .../eventstream}/no_event_stream.go | 0 internal/serde/stack.go | 44 +++++++ middleware/context.go | 17 --- schema.go | 29 +++-- serde.go | 20 +-- smithy-http-protocols/internal/cbor/codec.go | 20 --- .../internal/cbor/shape_deserializer.go | 66 ++++------ smithy-http-protocols/rpcv2/rpcv2.go | 74 +++++------ trait.go | 6 +- traits/http.go | 20 +-- traits/serde.go | 16 +-- traits/traits.go | 26 ++-- transport/http/protocol.go | 5 +- 36 files changed, 541 insertions(+), 588 deletions(-) rename aws-protocols/{awsjson10/awsjson10.go => awsjson/awsjson.go} (55%) delete mode 100644 aws-protocols/internal/json/codec.go delete mode 100644 aws-protocols/internal/xml/codec.go create mode 100644 eventstream/types.go rename {eventstream => internal/eventstream}/codec.go (58%) rename {eventstream => internal/eventstream}/no_event_stream.go (100%) create mode 100644 internal/serde/stack.go delete mode 100644 smithy-http-protocols/internal/cbor/codec.go diff --git a/aws-protocols/awsjson10/awsjson10.go b/aws-protocols/awsjson/awsjson.go similarity index 55% rename from aws-protocols/awsjson10/awsjson10.go rename to aws-protocols/awsjson/awsjson.go index d3cd51037..e3a6115e7 100644 --- a/aws-protocols/awsjson10/awsjson10.go +++ b/aws-protocols/awsjson/awsjson.go @@ -1,4 +1,4 @@ -package awsjson10 +package awsjson import ( "bytes" @@ -9,54 +9,73 @@ import ( "net/http" "github.com/aws/smithy-go" - awsjson "github.com/aws/smithy-go/aws-protocols/internal/json" - "github.com/aws/smithy-go/eventstream" + internaljson "github.com/aws/smithy-go/aws-protocols/internal/json" + internales "github.com/aws/smithy-go/internal/eventstream" + internalerrors "github.com/aws/smithy-go/internal/errors" smithyio "github.com/aws/smithy-go/io" "github.com/aws/smithy-go/middleware" + "github.com/aws/smithy-go/traits" smithyhttp "github.com/aws/smithy-go/transport/http" ) -// New returns an instance of the awsJson 1.0 protocol. -func New() *Protocol { +// ProtocolOptions configures aws.protocols#awsJson1_0. +type ProtocolOptions struct{} + +// New10 returns an instance of the awsJson 1.0 protocol. +func New10(service *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Protocol { + var o ProtocolOptions + for _, fn := range opts { + fn(&o) + } + _, qc := smithy.SchemaTrait[*traits.AWSQueryCompatible](service.Schema) return &Protocol{ - version: "1.0", - Codec: &eventstream.Codec{ - Codec: &awsjson.Codec{}, - ContentType: "application/json", + version: "1.0", + queryCompatible: qc, + serviceName: service.Schema.ID().Name, + Codec: &internales.Codec{ + Serializer: func() smithy.ShapeSerializer { return internaljson.NewShapeSerializer() }, + Deserializer: func(p []byte) smithy.ShapeDeserializer { return internaljson.NewShapeDeserializer(p) }, + ContentType: "application/json", }, } } // New11 returns an instance of the awsJson 1.1 protocol. -// -// TODO(serde2): figure out how we want to organize awsjson10 vs 11 -// TODO(serde2): this doesn't do query compatible yet -func New11() *Protocol { +func New11(service *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Protocol { + var o ProtocolOptions + for _, fn := range opts { + fn(&o) + } + _, qc := smithy.SchemaTrait[*traits.AWSQueryCompatible](service.Schema) return &Protocol{ - version: "1.1", - Codec: &eventstream.Codec{ - Codec: &awsjson.Codec{}, - ContentType: "application/json", + version: "1.1", + queryCompatible: qc, + serviceName: service.Schema.ID().Name, + Codec: &internales.Codec{ + Serializer: func() smithy.ShapeSerializer { return internaljson.NewShapeSerializer() }, + Deserializer: func(p []byte) smithy.ShapeDeserializer { return internaljson.NewShapeDeserializer(p) }, + ContentType: "application/json", }, } } -// Protocol implements aws.protocols#awsJson10. +// Protocol implements aws.protocols#awsJson1_0 and aws.protocols#awsJson1_1. type Protocol struct { - version string - UseQueryCompatible bool + version string + queryCompatible bool + serviceName string - *eventstream.Codec + *internales.Codec } var _ smithyhttp.ClientProtocol = (*Protocol)(nil) // ID identifies the protocol. -func (p *Protocol) ID() string { +func (p *Protocol) ID() smithy.ShapeID { if p.version == "1.1" { - return "aws.protocols#awsJson1_1" + return smithy.ShapeID{Namespace: "aws.protocols", Name: "awsJson1_1"} } - return "aws.protocols#awsJson1_0" + return smithy.ShapeID{Namespace: "aws.protocols", Name: "awsJson1_0"} } // SerializeRequest serializes a request for AWS Json 1.0. @@ -67,18 +86,18 @@ func (p *Protocol) SerializeRequest( req *smithyhttp.Request, ) error { req.Method = http.MethodPost - req.Header.Set("X-Amz-Target", fmt.Sprintf("%s.%s", middleware.GetServiceName(ctx), middleware.GetOperationName(ctx))) + req.Header.Set("X-Amz-Target", fmt.Sprintf("%s.%s", p.serviceName, middleware.GetOperationName(ctx))) if schema.IsInputEventStream() { req.Header.Set("Content-Type", "application/vnd.amazon.eventstream") return nil } req.Header.Set("Content-Type", "application/x-amz-json-"+p.version) - if p.UseQueryCompatible { - req.Header.Set("X-Amzn-Query-Compatible", "true") + if p.queryCompatible { + req.Header.Set("X-Amzn-Query-Mode", "true") } - ss := awsjson.NewShapeSerializer() + ss := internaljson.NewShapeSerializer() in.Serialize(ss) sreq, err := req.SetStream(bytes.NewReader(ss.Bytes())) @@ -115,7 +134,7 @@ func (p *Protocol) DeserializeResponse( return nil } - sd := awsjson.NewShapeDeserializer(payload) + sd := internaljson.NewShapeDeserializer(payload) if err := out.Deserialize(sd); err != nil { return &smithy.DeserializationError{Err: err} } @@ -150,7 +169,7 @@ func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithy body := io.TeeReader(errorBody, ringBuffer) decoder := json.NewDecoder(body) decoder.UseNumber() - bodyInfo, err := awsjson.GetProtocolErrorInfo(decoder) + bodyInfo, err := internaljson.GetProtocolErrorInfo(decoder) if err != nil { var snapshot bytes.Buffer io.Copy(&snapshot, ringBuffer) @@ -162,32 +181,47 @@ func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithy } errorBody.Seek(0, io.SeekStart) - if typ, ok := awsjson.ResolveProtocolErrorType(headerCode, bodyInfo); ok { + if typ, ok := internaljson.ResolveProtocolErrorType(headerCode, bodyInfo); ok { errorCode = typ } if len(bodyInfo.Message) != 0 { errorMessage = bodyInfo.Message } - errorCode = awsjson.SanitizeErrorCode(errorCode) + errorCode = internaljson.SanitizeErrorCode(errorCode) + + var queryCode string + var queryFault smithy.ErrorFault + if p.queryCompatible { + queryHeader := response.Header.Get("X-Amzn-Query-Error") + queryCode, queryFault = internalerrors.ParseQueryError(queryHeader) + } perr, ok := types.DeserializableError(errorCode) if !ok { + code := errorCode + if queryCode != "" { + code = queryCode + } return &smithy.GenericAPIError{ - Code: errorCode, + Code: code, Message: errorMessage, + Fault: queryFault, } - } errorBody.Seek(0, io.SeekStart) errorBytes, _ := io.ReadAll(errorBody) if len(errorBytes) > 0 { - deser := awsjson.NewShapeDeserializer(errorBytes) + deser := internaljson.NewShapeDeserializer(errorBytes) if err := perr.Deserialize(deser); err != nil { return &smithy.DeserializationError{Err: err} } } + if queryCode != "" { + internalerrors.SetErrorCodeOverride(perr, queryCode) + } + return perr } diff --git a/aws-protocols/awsquery/awsquery.go b/aws-protocols/awsquery/awsquery.go index 7498f7d72..dabc08e30 100644 --- a/aws-protocols/awsquery/awsquery.go +++ b/aws-protocols/awsquery/awsquery.go @@ -10,34 +10,37 @@ import ( "github.com/aws/smithy-go" internalquery "github.com/aws/smithy-go/aws-protocols/internal/query" internalxml "github.com/aws/smithy-go/aws-protocols/internal/xml" - "github.com/aws/smithy-go/eventstream" + internales "github.com/aws/smithy-go/internal/eventstream" "github.com/aws/smithy-go/middleware" "github.com/aws/smithy-go/traits" smithyhttp "github.com/aws/smithy-go/transport/http" ) +// ProtocolOptions configures aws.protocols#awsQuery. +type ProtocolOptions struct{} + // Protocol implements aws.protocols#awsQuery. type Protocol struct { - eventstream.NoEventStream - - // Service API version (e.g. "2020-01-08"), sent as the "Version" parameter - // in every request. - Version string + internales.NoEventStream - // the query protocols do not have a "codec", they just inline a query - // serializer and xml deserializer + version string } var _ smithyhttp.ClientProtocol = (*Protocol)(nil) -// New returns an instance of the awsQuery protocol. -func New(version string) *Protocol { - return &Protocol{Version: version} +// New returns an instance of the awsQuery protocol. The service version is +// pulled from the ServiceVersion trait on the service schema. +func New(service *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Protocol { + var o ProtocolOptions + for _, fn := range opts { + fn(&o) + } + return &Protocol{version: service.Version} } // ID identifies the protocol. -func (*Protocol) ID() string { - return "aws.protocols#awsQuery" +func (*Protocol) ID() smithy.ShapeID { + return smithy.ShapeID{Namespace: "aws.protocols", Name: "awsQuery"} } // SerializeRequest serializes a request for awsQuery. @@ -50,7 +53,7 @@ func (p *Protocol) SerializeRequest( req.Method = http.MethodPost req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - ss := internalquery.NewShapeSerializer(middleware.GetOperationName(ctx), p.Version) + ss := internalquery.NewShapeSerializer(middleware.GetOperationName(ctx), p.version) in.Serialize(ss) sreq, err := req.SetStream(bytes.NewReader(ss.Bytes())) diff --git a/aws-protocols/ec2query/ec2query.go b/aws-protocols/ec2query/ec2query.go index 273d1c093..052d0a93d 100644 --- a/aws-protocols/ec2query/ec2query.go +++ b/aws-protocols/ec2query/ec2query.go @@ -1,39 +1,45 @@ package ec2query import ( + "bytes" "context" "fmt" "io" "net/http" - "bytes" "github.com/aws/smithy-go" internalquery "github.com/aws/smithy-go/aws-protocols/internal/query" internalxml "github.com/aws/smithy-go/aws-protocols/internal/xml" - "github.com/aws/smithy-go/eventstream" + internales "github.com/aws/smithy-go/internal/eventstream" "github.com/aws/smithy-go/middleware" smithyhttp "github.com/aws/smithy-go/transport/http" ) +// ProtocolOptions configures aws.protocols#ec2Query. +type ProtocolOptions struct{} + // Protocol implements aws.protocols#ec2Query. type Protocol struct { - eventstream.NoEventStream + internales.NoEventStream - // Service API version (e.g. "2016-11-15"), sent as the "Version" - // parameter in every request. - Version string + version string } var _ smithyhttp.ClientProtocol = (*Protocol)(nil) -// New returns an instance of the ec2Query protocol. -func New(version string) *Protocol { - return &Protocol{Version: version} +// New returns an instance of the ec2Query protocol. The service version is +// pulled from the ServiceVersion trait on the service schema. +func New(service *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Protocol { + var o ProtocolOptions + for _, fn := range opts { + fn(&o) + } + return &Protocol{version: service.Version} } // ID identifies the protocol. -func (*Protocol) ID() string { - return "aws.protocols#ec2Query" +func (*Protocol) ID() smithy.ShapeID { + return smithy.ShapeID{Namespace: "aws.protocols", Name: "ec2Query"} } // SerializeRequest serializes a request for ec2Query. @@ -46,7 +52,7 @@ func (p *Protocol) SerializeRequest( req.Method = http.MethodPost req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - ss := internalquery.NewShapeSerializer(middleware.GetOperationName(ctx), p.Version, + ss := internalquery.NewShapeSerializer(middleware.GetOperationName(ctx), p.version, func(o *internalquery.ShapeSerializerOptions) { o.EC2Mode = true }, ) in.Serialize(ss) diff --git a/aws-protocols/internal/httpbinding/deserializer.go b/aws-protocols/internal/httpbinding/deserializer.go index de4bf5e13..760ef99c8 100644 --- a/aws-protocols/internal/httpbinding/deserializer.go +++ b/aws-protocols/internal/httpbinding/deserializer.go @@ -160,13 +160,13 @@ func (d *ShapeDeserializer) ReadStringPtr(s *smithy.Schema, v **string) error { *v = &hv return nil } + if _, ok := smithy.SchemaTrait[*traits.HTTPPayload](s); ok { + val := string(d.payload) + *v = &val + return nil + } } - var val string - if err := d.ReadString(s, &val); err != nil { - return err - } - *v = &val - return nil + return d.body.ReadStringPtr(s, v) } // ReadBool implements [smithy.ShapeDeserializer]. @@ -195,11 +195,13 @@ func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { // ReadBoolPtr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadBoolPtr(s *smithy.Schema, v **bool) error { + if !d.inBindings { + return d.body.ReadBoolPtr(s, v) + } var vv bool if err := d.ReadBool(s, &vv); err != nil { return err } - *v = &vv return nil } @@ -214,11 +216,13 @@ func (d *ShapeDeserializer) ReadInt8(s *smithy.Schema, v *int8) error { // ReadInt8Ptr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadInt8Ptr(s *smithy.Schema, v **int8) error { + if !d.inBindings { + return d.body.ReadInt8Ptr(s, v) + } var vv int8 if err := d.ReadInt8(s, &vv); err != nil { return err } - *v = &vv return nil } @@ -233,11 +237,13 @@ func (d *ShapeDeserializer) ReadInt16(s *smithy.Schema, v *int16) error { // ReadInt16Ptr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadInt16Ptr(s *smithy.Schema, v **int16) error { + if !d.inBindings { + return d.body.ReadInt16Ptr(s, v) + } var vv int16 if err := d.ReadInt16(s, &vv); err != nil { return err } - *v = &vv return nil } @@ -262,11 +268,13 @@ func (d *ShapeDeserializer) ReadInt32(s *smithy.Schema, v *int32) error { // ReadInt32Ptr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadInt32Ptr(s *smithy.Schema, v **int32) error { + if !d.inBindings { + return d.body.ReadInt32Ptr(s, v) + } var vv int32 if err := d.ReadInt32(s, &vv); err != nil { return err } - *v = &vv return nil } @@ -281,11 +289,13 @@ func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { // ReadInt64Ptr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadInt64Ptr(s *smithy.Schema, v **int64) error { + if !d.inBindings { + return d.body.ReadInt64Ptr(s, v) + } var vv int64 if err := d.ReadInt64(s, &vv); err != nil { return err } - *v = &vv return nil } @@ -300,11 +310,13 @@ func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error { // ReadFloat32Ptr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadFloat32Ptr(s *smithy.Schema, v **float32) error { + if !d.inBindings { + return d.body.ReadFloat32Ptr(s, v) + } var vv float32 if err := d.ReadFloat32(s, &vv); err != nil { return err } - *v = &vv return nil } @@ -319,11 +331,13 @@ func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { // ReadFloat64Ptr implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadFloat64Ptr(s *smithy.Schema, v **float64) error { + if !d.inBindings { + return d.body.ReadFloat64Ptr(s, v) + } var vv float64 if err := d.ReadFloat64(s, &vv); err != nil { return err } - *v = &vv return nil } diff --git a/aws-protocols/internal/json/codec.go b/aws-protocols/internal/json/codec.go deleted file mode 100644 index c139b4ef8..000000000 --- a/aws-protocols/internal/json/codec.go +++ /dev/null @@ -1,20 +0,0 @@ -package json - -import ( - "github.com/aws/smithy-go" -) - -// Codec is a JSON codec. -type Codec struct{} - -var _ smithy.Codec = (*Codec)(nil) - -// Serializer returns a JSON shape serializer. -func (c *Codec) Serializer() smithy.ShapeSerializer { - return NewShapeSerializer() -} - -// Deserializer returns a JSON shape deserializer. -func (c *Codec) Deserializer(p []byte) smithy.ShapeDeserializer { - return NewShapeDeserializer(p) -} diff --git a/aws-protocols/internal/json/shape_deserializer.go b/aws-protocols/internal/json/shape_deserializer.go index cb983a933..ea7c0a050 100644 --- a/aws-protocols/internal/json/shape_deserializer.go +++ b/aws-protocols/internal/json/shape_deserializer.go @@ -18,8 +18,8 @@ import ( // ShapeDeserializer implements unmarshaling of JSON into Smithy shapes. type ShapeDeserializer struct { dec *json.Decoder - head stack - opts ShapeDeserializerOptions + head jsonStack + opts Options // json.Decoder does not have a Peek() but we need to be able to // "lookahead" for conditionally pulling a null token out in ReadNil. @@ -27,24 +27,15 @@ type ShapeDeserializer struct { hasPeek bool } -// ShapeDeserializerOptions configures ShapeDeserializer. -type ShapeDeserializerOptions struct { - // UseJSONName controls whether the @jsonName trait is used to - // match JSON object keys to struct members. If false (the default), - // only the member name is used. Protocols like restJson1 set this - // to true, while RPC protocols like awsJson1_0 leave it false. - UseJSONName bool -} - // NewShapeDeserializer creates a new ShapeDeserializer. -func NewShapeDeserializer(p []byte, opts ...func(*ShapeDeserializerOptions)) *ShapeDeserializer { - o := ShapeDeserializerOptions{} +func NewShapeDeserializer(p []byte, opts ...func(*Options)) *ShapeDeserializer { + o := Options{} for _, fn := range opts { fn(&o) } dec := json.NewDecoder(bytes.NewReader(p)) dec.UseNumber() - return &ShapeDeserializer{dec: dec, opts: o} + return &ShapeDeserializer{dec: dec, head: newJSONStack(), opts: o} } var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil) diff --git a/aws-protocols/internal/json/shape_serializer.go b/aws-protocols/internal/json/shape_serializer.go index 38b0d16d4..ffb4ee434 100644 --- a/aws-protocols/internal/json/shape_serializer.go +++ b/aws-protocols/internal/json/shape_serializer.go @@ -16,13 +16,13 @@ import ( // ShapeSerializer implements marshaling of Smithy shapes to JSON. type ShapeSerializer struct { root *smithyjson.Encoder - head stack + head jsonStack - opts ShapeSerializerOptions + opts Options } -// ShapeSerializerOptions configures ShapeSerializer. -type ShapeSerializerOptions struct { +// Options configures JSON shape serialization and deserialization. +type Options struct { // Controls whether scalar zero values (numbers, strings, bools) are // written. If false (the default), zero values are not encoded. // @@ -34,7 +34,7 @@ type ShapeSerializerOptions struct { // name is used as-is. // // How this is set in practice depends on the protocol. RPC-style protocols - // like awsjson10 ignore @jsonName, REST-style protocols like restjson1 + // like awsjson ignore @jsonName, REST-style protocols like restjson1 // respect it. UseJSONName bool } @@ -42,13 +42,14 @@ type ShapeSerializerOptions struct { var _ smithy.ShapeSerializer = (*ShapeSerializer)(nil) // NewShapeSerializer creates a new ShapeSerializer. -func NewShapeSerializer(opts ...func(*ShapeSerializerOptions)) *ShapeSerializer { - o := ShapeSerializerOptions{} +func NewShapeSerializer(opts ...func(*Options)) *ShapeSerializer { + o := Options{} for _, fn := range opts { fn(&o) } return &ShapeSerializer{ root: smithyjson.NewEncoder(), + head: newJSONStack(), opts: o, } } @@ -547,28 +548,32 @@ func (s *ShapeSerializer) writeDocumentRaw(schema *smithy.Schema, p []byte) { } } -type stack struct { +type jsonStack struct { values []any } +func newJSONStack() jsonStack { + return jsonStack{values: make([]any, 0, 8)} +} + type empty struct{} -func (s *stack) Top() any { +func (s *jsonStack) Top() any { if len(s.values) == 0 { return empty{} } return s.values[len(s.values)-1] } -func (s *stack) Push(v any) { +func (s *jsonStack) Push(v any) { s.values = append(s.values, v) } -func (s *stack) Pop() { +func (s *jsonStack) Pop() { s.values = s.values[:len(s.values)-1] } -func (s *stack) Len() int { +func (s *jsonStack) Len() int { return len(s.values) } diff --git a/aws-protocols/internal/query/shape_serializer.go b/aws-protocols/internal/query/shape_serializer.go index 53513d768..84ef4bb50 100644 --- a/aws-protocols/internal/query/shape_serializer.go +++ b/aws-protocols/internal/query/shape_serializer.go @@ -12,6 +12,7 @@ import ( "github.com/aws/smithy-go" "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/internal/serde" smithytime "github.com/aws/smithy-go/time" "github.com/aws/smithy-go/traits" ) @@ -46,7 +47,7 @@ type ShapeSerializer struct { opts ShapeSerializerOptions values url.Values - stack []serCtx + stack serde.Stack[serCtx] currPrefix string // runs as values are written e.g. for list } @@ -84,7 +85,7 @@ func NewShapeSerializer(action, version string, opts ...func(*ShapeSerializerOpt v := url.Values{} v.Set("Action", action) v.Set("Version", version) - return &ShapeSerializer{values: v, opts: o} + return &ShapeSerializer{values: v, stack: serde.NewStack[serCtx](), opts: o} } // Bytes returns the encoded query string as bytes. @@ -110,25 +111,11 @@ func (s *ShapeSerializer) Bytes() []byte { } func (s *ShapeSerializer) top() ctxKind { - if len(s.stack) == 0 { + t := s.stack.Top() + if t == nil { return ctxKindNone } - return s.stack[len(s.stack)-1].kind -} - -func (s *ShapeSerializer) push(ctx serCtx) { - s.stack = append(s.stack, ctx) -} - -func (s *ShapeSerializer) pop() serCtx { - n := len(s.stack) - v := s.stack[n-1] - s.stack = s.stack[:n-1] - return v -} - -func (s *ShapeSerializer) topCtx() *serCtx { - return &s.stack[len(s.stack)-1] + return t.kind } func (s *ShapeSerializer) withWriteZero(fn func()) { @@ -142,7 +129,7 @@ func (s *ShapeSerializer) skipZeroValue() bool { if s.opts.WriteZeroValues { return false } - if len(s.stack) == 0 { + if s.stack.Len() == 0 { return false } @@ -184,7 +171,7 @@ func (s *ShapeSerializer) resolveKey(schema *smithy.Schema) string { valPrefix := s.consumeMapValue() return valPrefix case ctxKindList: - ctx := s.topCtx() + ctx := s.stack.Top() ctx.listIndex++ return s.currPrefix + "." + strconv.Itoa(ctx.listIndex) default: @@ -208,8 +195,9 @@ func (s *ShapeSerializer) writeValue(key, value string) { } func (s *ShapeSerializer) bufferMapEntry(key, value string) bool { - for i := len(s.stack) - 1; i >= 0; i-- { - ctx := &s.stack[i] + vals := s.stack.Values() + for i := len(vals) - 1; i >= 0; i-- { + ctx := &vals[i] if ctx.kind != ctxKindMap { continue } @@ -389,19 +377,19 @@ func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema) { valPrefix := s.consumeMapValue() s.currPrefix, saved = valPrefix, s.currPrefix case ctxKindList: - ctx := s.topCtx() + ctx := s.stack.Top() ctx.listIndex++ s.currPrefix = s.currPrefix + "." + strconv.Itoa(ctx.listIndex) default: s.appendMemberPrefix(schema) } - s.push(serCtx{kind: ctxKindStruct, prefix: saved}) + s.stack.Push(serCtx{kind: ctxKindStruct, prefix: saved}) } // CloseStruct implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) CloseStruct() { - ctx := s.pop() + ctx := s.stack.Pop() s.currPrefix = ctx.prefix } @@ -410,7 +398,7 @@ func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema, v smithy.Se saved := s.currPrefix s.appendMemberPrefix(schema) if s.top() == ctxKindMapValue { - s.pop() + s.stack.Pop() } v.Serialize(s) s.currPrefix = saved @@ -433,11 +421,11 @@ func (s *ShapeSerializer) enterContainer(schema *smithy.Schema) string { // with the map prefix. Pop ctxMapValue and use its saved prefix // as the restore point — s.prefix (the value path) is already // the correct working prefix. - ctx := s.pop() + ctx := s.stack.Pop() saved = ctx.prefix return saved case ctxKindList: - ctx := s.topCtx() + ctx := s.stack.Top() ctx.listIndex++ s.currPrefix = s.currPrefix + "." + strconv.Itoa(ctx.listIndex) } @@ -466,7 +454,7 @@ func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { s.currPrefix = s.currPrefix + "." + locName } - s.push(serCtx{ + s.stack.Push(serCtx{ kind: ctxKindList, flattened: flattened, prefix: saved, @@ -480,7 +468,7 @@ func (s *ShapeSerializer) CloseList() { return } - ctx := s.pop() + ctx := s.stack.Pop() if ctx.listIndex == 0 && !s.opts.EC2Mode { s.writeValue(ctx.listPrefix, "") } @@ -511,7 +499,7 @@ func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { } } - s.push(serCtx{ + s.stack.Push(serCtx{ kind: ctxKindMap, flattened: flattened, prefix: saved, @@ -522,7 +510,7 @@ func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { // WriteKey implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteKey(_ *smithy.Schema, key string) { - ctx := s.topCtx() + ctx := s.stack.Top() ctx.mapIndex++ prefix := s.currPrefix + "." + strconv.Itoa(ctx.mapIndex) @@ -535,7 +523,7 @@ func (s *ShapeSerializer) WriteKey(_ *smithy.Schema, key string) { // Push ctxMapValue with the current prefix so the next value write can // restore it. Set s.prefix to the value path. - s.push(serCtx{kind: ctxKindMapValue, prefix: s.currPrefix}) + s.stack.Push(serCtx{kind: ctxKindMapValue, prefix: s.currPrefix}) s.currPrefix = prefix + "." + ctx.mapValueName } @@ -545,7 +533,7 @@ func (s *ShapeSerializer) CloseMap() { return } - ctx := s.pop() + ctx := s.stack.Pop() // Sort entries by map key for deterministic output. sort.Slice(ctx.mapBuf, func(i, j int) bool { @@ -579,7 +567,7 @@ func (s *ShapeSerializer) WriteDocument(_ *smithy.Schema, _ document.Value) { } func (s *ShapeSerializer) consumeMapValue() string { - ctx := s.pop() + ctx := s.stack.Pop() valPrefix := s.currPrefix s.currPrefix = ctx.prefix return valPrefix diff --git a/aws-protocols/internal/xml/codec.go b/aws-protocols/internal/xml/codec.go deleted file mode 100644 index e2e6371f5..000000000 --- a/aws-protocols/internal/xml/codec.go +++ /dev/null @@ -1,27 +0,0 @@ -package xml - -import ( - "github.com/aws/smithy-go" -) - -// Codec implements [smithy.Codec] for XML. -type Codec struct { - opts []func(*ShapeSerializerOptions) -} - -// NewCodec returns an XML codec with the given serializer options. -func NewCodec(opts ...func(*ShapeSerializerOptions)) *Codec { - return &Codec{opts: opts} -} - -var _ smithy.Codec = (*Codec)(nil) - -// Serializer returns an XML shape serializer. -func (c *Codec) Serializer() smithy.ShapeSerializer { - return NewShapeSerializer(c.opts...) -} - -// Deserializer returns an XML shape deserializer. -func (c *Codec) Deserializer(p []byte) smithy.ShapeDeserializer { - return NewShapeDeserializer(p) -} diff --git a/aws-protocols/internal/xml/element_name.go b/aws-protocols/internal/xml/element_name.go index f18dd6311..61a778f30 100644 --- a/aws-protocols/internal/xml/element_name.go +++ b/aws-protocols/internal/xml/element_name.go @@ -17,7 +17,7 @@ func (s *ShapeSerializer) ename(schema *smithy.Schema) string { // we don't use the one on the target if t, ok := smithy.SchemaDirectTrait[*traits.XMLName](schema); ok { return t.Name - } else if len(s.stack) == 0 { // the root + } else if s.stack.Len() == 0 { // the root return schema.ID().Name } @@ -26,7 +26,7 @@ func (s *ShapeSerializer) ename(schema *smithy.Schema) string { // wraps ename to check for the context of "am i in a list or map" func (s *ShapeSerializer) ctxEname(schema *smithy.Schema) string { - if top := s.top(); top != nil { + if top := s.stack.Top(); top != nil { switch top.kind { case ctxKindList: return top.itemName @@ -58,7 +58,7 @@ func (s *ShapeSerializer) structEname(schema *smithy.Schema) string { // resolution for @xmlNamespace, which is generally sane func (s *ShapeSerializer) xmlns(schema *smithy.Schema) *traits.XMLNamespace { - if top := s.top(); top != nil { + if top := s.stack.Top(); top != nil { switch { case top.flat && (top.kind == ctxKindList || top.kind == ctxKindMap): // flattened lists/maps have no wrapper, inherit the diff --git a/aws-protocols/internal/xml/shape_deserializer.go b/aws-protocols/internal/xml/shape_deserializer.go index deba3f446..c760aac03 100644 --- a/aws-protocols/internal/xml/shape_deserializer.go +++ b/aws-protocols/internal/xml/shape_deserializer.go @@ -12,6 +12,7 @@ import ( "github.com/aws/smithy-go" "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/internal/serde" smithytime "github.com/aws/smithy-go/time" "github.com/aws/smithy-go/traits" ) @@ -76,7 +77,7 @@ type deserCtx struct { type ShapeDeserializer struct { dec *xml.Decoder peeked xml.Token - stack []deserCtx + stack serde.Stack[deserCtx] // most recent start element we saw in the token stream, when we go into a // struct context we grab it so we can read any @xmlAttributes @@ -89,28 +90,11 @@ var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil) // NewShapeDeserializer returns a new ShapeDeserializer. func NewShapeDeserializer(p []byte) *ShapeDeserializer { return &ShapeDeserializer{ - dec: xml.NewDecoder(bytes.NewReader(p)), + dec: xml.NewDecoder(bytes.NewReader(p)), + stack: serde.NewStack[deserCtx](), } } -func (d *ShapeDeserializer) push(ctx deserCtx) { - d.stack = append(d.stack, ctx) -} - -func (d *ShapeDeserializer) pop() deserCtx { - n := len(d.stack) - v := d.stack[n-1] - d.stack = d.stack[:n-1] - return v -} - -func (d *ShapeDeserializer) top() *deserCtx { - if len(d.stack) == 0 { - return nil - } - return &d.stack[len(d.stack)-1] -} - func xmlMemberName(schema *smithy.Schema) string { if t, ok := smithy.SchemaDirectTrait[*traits.XMLName](schema); ok { return t.Name @@ -139,13 +123,13 @@ func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error { } start := *d.currStart - d.push(deserCtx{kind: ctxKindStruct, schema: s, startElem: start}) + d.stack.Push(deserCtx{kind: ctxKindStruct, schema: s, startElem: start}) return nil } // ReadStructMember implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { - ctx := d.top() + ctx := d.stack.Top() if ctx == nil || ctx.kind != ctxKindStruct { return nil, fmt.Errorf("ReadStructMember called without ReadStruct") } @@ -160,7 +144,7 @@ func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { return nil, err } if !ok { - d.pop() + d.stack.Pop() return nil, nil } @@ -220,7 +204,7 @@ func findAttrMember(schema *smithy.Schema, elemName string) *smithy.Schema { // ReadList implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { _, flattened := smithy.SchemaTrait[*traits.XMLFlattened](s) - d.push(deserCtx{ + d.stack.Push(deserCtx{ kind: ctxKindList, schema: s, flattened: flattened, @@ -231,7 +215,7 @@ func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { // ReadListItem implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadListItem(_ *smithy.Schema) (bool, error) { - ctx := d.top() + ctx := d.stack.Top() if ctx.flattened { return d.readFlatListItem() } @@ -246,7 +230,7 @@ func (d *ShapeDeserializer) readWrappedListItem() (bool, error) { return false, err } if !ok { - d.pop() + d.stack.Pop() return false, nil } @@ -254,7 +238,7 @@ func (d *ShapeDeserializer) readWrappedListItem() (bool, error) { } func (d *ShapeDeserializer) readFlatListItem() (bool, error) { - ctx := d.top() + ctx := d.stack.Top() if ctx.first { ctx.first = false return true, nil @@ -275,11 +259,11 @@ func (d *ShapeDeserializer) readFlatListItem() (bool, error) { } d.peeked = t - d.pop() + d.stack.Pop() return false, nil case xml.EndElement: d.peeked = t - d.pop() + d.stack.Pop() return false, nil } } @@ -288,7 +272,7 @@ func (d *ShapeDeserializer) readFlatListItem() (bool, error) { // ReadMap implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadMap(s *smithy.Schema) error { _, flattened := smithy.SchemaTrait[*traits.XMLFlattened](s) - d.push(deserCtx{ + d.stack.Push(deserCtx{ kind: ctxKindMap, schema: s, flattened: flattened, @@ -299,7 +283,7 @@ func (d *ShapeDeserializer) ReadMap(s *smithy.Schema) error { // ReadMapKey implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadMapKey(ks *smithy.Schema) (string, bool, error) { - ctx := d.top() + ctx := d.stack.Top() if ctx.inMapEntry { ctx.inMapEntry = false if _, _, err := d.nextStart(); err != nil { @@ -323,7 +307,7 @@ func (d *ShapeDeserializer) readWrappedMapKey(ks, vs *smithy.Schema) (string, bo return "", false, err } if !a { - d.pop() + d.stack.Pop() return "", false, nil } @@ -340,7 +324,7 @@ func (d *ShapeDeserializer) readWrappedMapKey(ks, vs *smithy.Schema) (string, bo } func (d *ShapeDeserializer) readFlatMapKey(ks, vs *smithy.Schema) (string, bool, error) { - ctx := d.top() + ctx := d.stack.Top() if ctx.first { ctx.first = false return d.readEntry(ks, vs) @@ -360,18 +344,18 @@ func (d *ShapeDeserializer) readFlatMapKey(ks, vs *smithy.Schema) (string, bool, } d.peeked = t - d.pop() + d.stack.Pop() return "", false, nil case xml.EndElement: d.peeked = t - d.pop() + d.stack.Pop() return "", false, nil } } } func (d *ShapeDeserializer) readEntry(ks, vs *smithy.Schema) (string, bool, error) { - ctx := d.top() + ctx := d.stack.Top() kname := "key" if xn, ok := smithy.SchemaDirectTrait[*traits.XMLName](ks); ok { diff --git a/aws-protocols/internal/xml/shape_serializer.go b/aws-protocols/internal/xml/shape_serializer.go index 76ad95e22..a35d91f61 100644 --- a/aws-protocols/internal/xml/shape_serializer.go +++ b/aws-protocols/internal/xml/shape_serializer.go @@ -9,6 +9,7 @@ import ( "github.com/aws/smithy-go" "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/internal/serde" smithytime "github.com/aws/smithy-go/time" "github.com/aws/smithy-go/traits" ) @@ -34,9 +35,7 @@ type serCtx struct { type ShapeSerializer struct { w *writer - // TODO(serde2): SerdeStack[T], obviously this primitive has been reduped a - // bunch at this point - stack []serCtx + stack serde.Stack[serCtx] opts ShapeSerializerOptions } @@ -56,8 +55,9 @@ func NewShapeSerializer(opts ...func(*ShapeSerializerOptions)) *ShapeSerializer fn(&o) } return &ShapeSerializer{ - w: newWriter(), - opts: o, + w: newWriter(), + stack: serde.NewStack[serCtx](), + opts: o, } } @@ -222,12 +222,12 @@ func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema) { w: s.w, } s.w = newWriter() - s.push(ctx) + s.stack.Push(ctx) } // CloseStruct implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) CloseStruct() { - ctx := s.pop() + ctx := s.stack.Pop() var ns *traits.XMLNamespace if isPayload(ctx.schema) { @@ -237,7 +237,7 @@ func (s *ShapeSerializer) CloseStruct() { } // special case for the root struct where the service set a namespace - if ns == nil && len(s.stack) == 0 && s.opts.RootNamespaceURI != "" { + if ns == nil && s.stack.Len() == 0 && s.opts.RootNamespaceURI != "" { ns = &traits.XMLNamespace{ URI: s.opts.RootNamespaceURI, Prefix: s.opts.RootNamespacePrefix, @@ -277,7 +277,7 @@ func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { ename = "" } - s.push(serCtx{ + s.stack.Push(serCtx{ kind: ctxKindList, wrapperName: ename, itemName: iname, @@ -288,7 +288,7 @@ func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { // CloseList implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) CloseList() { - ctx := s.pop() + ctx := s.stack.Pop() if !ctx.flat { s.w.writeEnd(ctx.wrapperName) } @@ -308,7 +308,7 @@ func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { s.w.writeStart(wrapperName, ns, nil) } - s.push(serCtx{ + s.stack.Push(serCtx{ kind: ctxKindMap, wrapperName: wrapperName, itemName: itemName, @@ -319,7 +319,7 @@ func (s *ShapeSerializer) WriteMap(schema *smithy.Schema) { // WriteKey implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteKey(schema *smithy.Schema, k string) { - top := s.top() + top := s.stack.Top() if top == nil || top.kind != ctxKindMap { return } @@ -342,7 +342,7 @@ func (s *ShapeSerializer) WriteKey(schema *smithy.Schema, k string) { // CloseMap implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) CloseMap() { - ctx := s.pop() + ctx := s.stack.Pop() if ctx.inMapEntry { s.w.writeEnd(ctx.itemName) @@ -358,24 +358,6 @@ func (s *ShapeSerializer) WriteDocument(schema *smithy.Schema, v document.Value) panic("WriteDocument not supported for XML") } -func (s *ShapeSerializer) push(ctx serCtx) { - s.stack = append(s.stack, ctx) -} - -func (s *ShapeSerializer) pop() serCtx { - n := len(s.stack) - ctx := s.stack[n-1] - s.stack = s.stack[:n-1] - return ctx -} - -func (s *ShapeSerializer) top() *serCtx { - if len(s.stack) == 0 { - return nil - } - return &s.stack[len(s.stack)-1] -} - func (s *ShapeSerializer) writeScalar(schema *smithy.Schema, v string) { if s.bufferAttribute(schema, v) { return @@ -392,7 +374,7 @@ func (s *ShapeSerializer) bufferAttribute(schema *smithy.Schema, v string) bool return false } - top := s.top() + top := s.stack.Top() if top == nil || top.kind != ctxKindStruct { return false } diff --git a/aws-protocols/restjson1/restjson1.go b/aws-protocols/restjson1/restjson1.go index 558c267dd..23e88182f 100644 --- a/aws-protocols/restjson1/restjson1.go +++ b/aws-protocols/restjson1/restjson1.go @@ -10,16 +10,24 @@ import ( "github.com/aws/smithy-go" internalhttpbinding "github.com/aws/smithy-go/aws-protocols/internal/httpbinding" internaljson "github.com/aws/smithy-go/aws-protocols/internal/json" - "github.com/aws/smithy-go/eventstream" + internales "github.com/aws/smithy-go/internal/eventstream" smithyio "github.com/aws/smithy-go/io" smithyhttp "github.com/aws/smithy-go/transport/http" ) +// ProtocolOptions configures aws.protocols#restJson1. +type ProtocolOptions struct{} + // New returns an instance of the aws.protocols#restJson1 protocol. -func New() *Protocol { +func New(_ *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Protocol { + var o ProtocolOptions + for _, fn := range opts { + fn(&o) + } return &Protocol{ - Codec: &eventstream.Codec{ - Codec: &internaljson.Codec{}, + Codec: &internales.Codec{ + Serializer: func() smithy.ShapeSerializer { return internaljson.NewShapeSerializer() }, + Deserializer: func(p []byte) smithy.ShapeDeserializer { return internaljson.NewShapeDeserializer(p) }, ContentType: "application/json", }, } @@ -27,14 +35,14 @@ func New() *Protocol { // Protocol implements aws.protocols#restJson1. type Protocol struct { - *eventstream.Codec + *internales.Codec } var _ smithyhttp.ClientProtocol = (*Protocol)(nil) // ID identifies the protocol. -func (*Protocol) ID() string { - return "aws.protocols#restJson1" +func (*Protocol) ID() smithy.ShapeID { + return smithy.ShapeID{Namespace: "aws.protocols", Name: "restJson1"} } // SerializeRequest serializes a request for restJson1. @@ -44,7 +52,7 @@ func (p *Protocol) SerializeRequest( in smithy.Serializable, req *smithyhttp.Request, ) error { - serializer, err := internalhttpbinding.NewShapeSerializer(op.Schema, req, internaljson.NewShapeSerializer(sUseJSONName)) + serializer, err := internalhttpbinding.NewShapeSerializer(op.Schema, req, internaljson.NewShapeSerializer(useJSONName)) if err != nil { return err } @@ -172,14 +180,9 @@ func bd(payload []byte) smithy.ShapeDeserializer { if len(payload) == 0 { payload = []byte("{}") } - return internaljson.NewShapeDeserializer(payload, dUseJSONName) -} - -// TODO(serde2): can we just have one struct? -func sUseJSONName(o *internaljson.ShapeSerializerOptions) { - o.UseJSONName = true + return internaljson.NewShapeDeserializer(payload, useJSONName) } -func dUseJSONName(o *internaljson.ShapeDeserializerOptions) { +func useJSONName(o *internaljson.Options) { o.UseJSONName = true } diff --git a/aws-protocols/restxml/restxml.go b/aws-protocols/restxml/restxml.go index 596ebb558..ed420d9e0 100644 --- a/aws-protocols/restxml/restxml.go +++ b/aws-protocols/restxml/restxml.go @@ -9,50 +9,57 @@ import ( "github.com/aws/smithy-go" internalhttpbinding "github.com/aws/smithy-go/aws-protocols/internal/httpbinding" internalxml "github.com/aws/smithy-go/aws-protocols/internal/xml" - "github.com/aws/smithy-go/eventstream" + internales "github.com/aws/smithy-go/internal/eventstream" + "github.com/aws/smithy-go/traits" smithyhttp "github.com/aws/smithy-go/transport/http" ) // Protocol implements aws.protocols#restXml. type Protocol struct { - *eventstream.Codec + *internales.Codec seropts func(*internalxml.ShapeSerializerOptions) } +// ProtocolOptions configures aws.protocols#restXml. +type ProtocolOptions struct{} + var _ smithyhttp.ClientProtocol = (*Protocol)(nil) -// New returns an instance of the aws.protocols#restXml protocol. -func New() *Protocol { - return &Protocol{ - Codec: &eventstream.Codec{ - Codec: internalxml.NewCodec(), - ContentType: "application/xml", - }, +// New returns an instance of the aws.protocols#restXml protocol. If the +// service schema carries an xmlNamespace trait, it is applied as the root +// xmlns attribute on serialized request bodies. +func New(service *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Protocol { + var o ProtocolOptions + for _, fn := range opts { + fn(&o) + } + var xmlOpts func(*internalxml.ShapeSerializerOptions) + if ns, ok := smithy.SchemaTrait[*traits.XMLNamespace](service.Schema); ok { + xmlOpts = func(o *internalxml.ShapeSerializerOptions) { + o.RootNamespaceURI = ns.URI + o.RootNamespacePrefix = ns.Prefix + } } -} -// NewWithNamespace returns an instance of the aws.protocols#restXml protocol -// configured with the given XML namespace URI (and optional prefix) to emit -// as an xmlns attribute on the request body's root element. Corresponds to -// the service-level @xmlNamespace trait. -func NewWithNamespace(uri, prefix string) *Protocol { - xmlOpts := func(o *internalxml.ShapeSerializerOptions) { - o.RootNamespaceURI = uri - o.RootNamespacePrefix = prefix - } return &Protocol{ - Codec: &eventstream.Codec{ - Codec: internalxml.NewCodec(xmlOpts), - ContentType: "application/xml", + Codec: &internales.Codec{ + Serializer: func() smithy.ShapeSerializer { + if xmlOpts != nil { + return internalxml.NewShapeSerializer(xmlOpts) + } + return internalxml.NewShapeSerializer() + }, + Deserializer: func(p []byte) smithy.ShapeDeserializer { return internalxml.NewShapeDeserializer(p) }, + ContentType: "application/xml", }, seropts: xmlOpts, } } // ID identifies the protocol. -func (*Protocol) ID() string { - return "aws.protocols#restXml" +func (*Protocol) ID() smithy.ShapeID { + return smithy.ShapeID{Namespace: "aws.protocols", Name: "restXml"} } // HasInitialEventMessage implements [smithyhttp.ClientProtocol]. diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java index a2a6365c2..4bf88b23b 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java @@ -295,6 +295,16 @@ void execute() { new SchemaGenerator(ctx, op).accept(writer); } + // Generate the service schema: first the underlying *Schema + // (which carries traits like xmlNamespace), then wrap it in + // NewServiceSchema with the service version. + new SchemaGenerator(ctx, service).accept(writer); + writer.addUseImports(SmithyGoDependency.SMITHY); + writer.write("var Service = smithy.NewServiceSchema($L, $S)", + SchemaGenerator.getSchemaName(service, service), + service.getVersion()); + writer.write(""); + writer.write(""); writer.writeDocs("Initialize schema members after all schemas are declared to avoid initialization cycles"); writer.openBlock("func init() {", "}", () -> { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java index 628621f5b..13693d657 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/DefaultTraitGenerators.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.Map; +import software.amazon.smithy.aws.traits.protocols.AwsQueryCompatibleTrait; import software.amazon.smithy.aws.traits.protocols.AwsQueryErrorTrait; import software.amazon.smithy.aws.traits.protocols.Ec2QueryNameTrait; import software.amazon.smithy.go.codegen.trait.BackfilledInputOutputTrait; @@ -84,6 +85,7 @@ public class DefaultTraitGenerators { GENERATORS.put(AwsQueryErrorTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("AWSQueryError"), "ErrorCode", AwsQueryErrorTrait::getCode, "StatusCode", AwsQueryErrorTrait::getHttpResponseCode)); + GENERATORS.put(AwsQueryCompatibleTrait.ID, new SimpleTraitGenerator(SMITHY_TRAITS.struct("AWSQueryCompatible"))); GENERATORS.put(Ec2QueryNameTrait.ID, new SimpleTraitGenerator<>(SMITHY_TRAITS.struct("EC2QueryName"), "Name", Ec2QueryNameTrait::getValue)); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java index f051a8446..c5e191e9b 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java @@ -30,7 +30,6 @@ import java.util.stream.Stream; import software.amazon.smithy.aws.traits.protocols.AwsJson1_0Trait; import software.amazon.smithy.aws.traits.protocols.AwsJson1_1Trait; -import software.amazon.smithy.aws.traits.protocols.AwsQueryCompatibleTrait; import software.amazon.smithy.aws.traits.protocols.AwsQueryTrait; import software.amazon.smithy.aws.traits.protocols.Ec2QueryTrait; import software.amazon.smithy.aws.traits.protocols.RestJson1Trait; @@ -56,7 +55,6 @@ import software.amazon.smithy.model.knowledge.TopDownIndex; import software.amazon.smithy.model.shapes.ServiceShape; import software.amazon.smithy.model.shapes.ShapeId; -import software.amazon.smithy.model.traits.XmlNamespaceTrait; import software.amazon.smithy.protocol.traits.Rpcv2CborTrait; import software.amazon.smithy.utils.MapUtils; @@ -147,8 +145,7 @@ private Writable generateMetadata() { return goTemplate(""" const ServiceID = $S const ServiceAPIVersion = $S - const serviceName = $S - """, serviceId, service.getVersion(), service.getId().getName()); + """, serviceId, service.getVersion()); } private Writable generateClient() { @@ -266,8 +263,10 @@ func New(options $options:L, optFns ...func(*$options:L)) *$client:L { private Writable generateExperimentalSerdeResolvers() { ensureSupportedProtocol(); - // TODO(serde2) dynamically resolve a symbol based on traits - return goTemplate("options.Protocol = $W", resolveDefaultProtocol()); + return writer -> { + writer.addImport(settings.getModuleName() + "/schemas", "schemas"); + writer.write("options.Protocol = $W", resolveDefaultProtocol()); + }; } private Writable resolveDefaultProtocol() { @@ -278,48 +277,31 @@ private Writable resolveDefaultProtocol() { .get(); if (preferred.equals(AwsJson1_0Trait.ID)) { - return goTemplate("$T()", SmithyGoDependency.SMITHY_AWS_PROTOCOLS_JSON10.func("New")); + return goTemplate("$T(schemas.Service)", + SmithyGoDependency.SMITHY_AWS_PROTOCOLS_JSON10.func("New10")); } else if (preferred.equals(AwsJson1_1Trait.ID)) { - return goTemplate("$T()", SmithyGoDependency.SMITHY_AWS_PROTOCOLS_JSON10.func("New11")); + return goTemplate("$T(schemas.Service)", + SmithyGoDependency.SMITHY_AWS_PROTOCOLS_JSON10.func("New11")); } else if (preferred.equals(RestJson1Trait.ID)) { - return goTemplate("$T()", SmithyGoDependency.SMITHY_AWS_PROTOCOLS_RESTJSON1.func("New")); + return goTemplate("$T(schemas.Service)", + SmithyGoDependency.SMITHY_AWS_PROTOCOLS_RESTJSON1.func("New")); } else if (preferred.equals(Rpcv2CborTrait.ID)) { - return goTemplate("$T($W)", - SmithyGoDependency.SMITHY_HTTP_PROTOCOLS_RPCV2.func("NewCBOR"), - service.hasTrait(AwsQueryCompatibleTrait.class) - ? goTemplate("$T", SmithyGoDependency.SMITHY_HTTP_PROTOCOLS_RPCV2.valueSymbol("UseQueryCompatible")) - : emptyGoTemplate() - ); + return goTemplate("$T(schemas.Service)", + SmithyGoDependency.SMITHY_HTTP_PROTOCOLS_RPCV2.func("NewCBOR")); } else if (preferred.equals(AwsQueryTrait.ID)) { - return goTemplate("$T($S)", - SmithyGoDependency.SMITHY_AWS_PROTOCOLS_AWSQUERY.func("New"), - service.getVersion() - ); + return goTemplate("$T(schemas.Service)", + SmithyGoDependency.SMITHY_AWS_PROTOCOLS_AWSQUERY.func("New")); } else if (preferred.equals(Ec2QueryTrait.ID)) { - return goTemplate("$T($S)", - SmithyGoDependency.SMITHY_AWS_PROTOCOLS_EC2QUERY.func("New"), - service.getVersion() - ); + return goTemplate("$T(schemas.Service)", + SmithyGoDependency.SMITHY_AWS_PROTOCOLS_EC2QUERY.func("New")); } else if (preferred.equals(RestXmlTrait.ID)) { - return restXmlNew(); + return goTemplate("$T(schemas.Service)", + SmithyGoDependency.SMITHY_AWS_PROTOCOLS_RESTXML.func("New")); } else { return goTemplate("nil"); } } - private Writable restXmlNew() { - var ns = service.getTrait(XmlNamespaceTrait.class); - if (ns.isEmpty()) { - return goTemplate("$T()", - SmithyGoDependency.SMITHY_AWS_PROTOCOLS_RESTXML.func("New")); - } - var t = ns.get(); - return goTemplate("$T($S, $S)", - SmithyGoDependency.SMITHY_AWS_PROTOCOLS_RESTXML.func("NewWithNamespace"), - t.getUri(), - t.getPrefix().orElse("")); - } - private Writable generateGetOptions() { var docs = autoDocTemplate(""" Options returns a copy of the client configuration. @@ -426,7 +408,6 @@ private Writable generateInvokeOperation() { ) { ctx = middleware.ClearStackValues(ctx) ctx = middleware.WithServiceID(ctx, ServiceID) - ctx = middleware.WithServiceName(ctx, serviceName) ctx = middleware.WithOperationName(ctx, opID) $newStack:W diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java index c18d78b95..fbfd3e320 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SmithyGoDependency.java @@ -84,7 +84,7 @@ public final class SmithyGoDependency { public static final GoDependency SMITHY_METRICS = smithy("metrics"); public static final GoDependency SMITHY_AWS_PROTOCOLS = smithy("aws-protocols"); - public static final GoDependency SMITHY_AWS_PROTOCOLS_JSON10 = smithy("aws-protocols/awsjson10"); + public static final GoDependency SMITHY_AWS_PROTOCOLS_JSON10 = smithy("aws-protocols/awsjson"); public static final GoDependency SMITHY_AWS_PROTOCOLS_RESTJSON1 = smithy("aws-protocols/restjson1"); public static final GoDependency SMITHY_AWS_PROTOCOLS_AWSQUERY = smithy("aws-protocols/awsquery"); public static final GoDependency SMITHY_AWS_PROTOCOLS_EC2QUERY = smithy("aws-protocols/ec2query"); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java index 7e0298f4e..37d948377 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java @@ -3,6 +3,7 @@ import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; import java.util.Map; +import software.amazon.smithy.model.loader.Prelude; import software.amazon.smithy.model.shapes.StructureShape; public class TypeRegistry implements Writable { @@ -17,6 +18,9 @@ public void accept(GoWriter writer) { var shapes = ctx.serdeShapes(StructureShape.class).stream().toList(); writer.addUseImports(SmithyGoDependency.SMITHY); + if (shapes.stream().anyMatch(Prelude::isPublicPreludeShape)) { + writer.addUseImports(SmithyGoDependency.SMITHY_PRELUDE); + } writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); writer.writeGoTemplate(""" // TypeRegistry is the type registry for this service. @@ -31,8 +35,8 @@ public void accept(GoWriter writer) { private Writable renderEntry(StructureShape shape) { return goTemplate(""" $S: &smithy.TypeRegistryEntry{ - Schema: schemas.$L, + Schema: $L, New: func() any { return &$T{} }, - },""", shape.getId().toString(), SchemaGenerator.getSchemaName(shape, ctx.service()), ctx.symbolProvider().toSymbol(shape)); + },""", shape.getId().toString(), SchemaGenerator.getSchemaRef(shape, ctx.service()), ctx.symbolProvider().toSymbol(shape)); } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java index 6c250f214..02c763f39 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java @@ -10,6 +10,7 @@ import software.amazon.smithy.go.codegen.Writable; import software.amazon.smithy.go.codegen.knowledge.GoPointableIndex; import software.amazon.smithy.go.codegen.util.ShapeUtil; +import software.amazon.smithy.model.loader.Prelude; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.StructureShape; @@ -30,6 +31,9 @@ public StructureDeserializer(GoCodegenContext ctx, StructureShape shape) { @Override public void accept(GoWriter writer) { writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); + if (Prelude.isPublicPreludeShape(shape)) { + writer.addUseImports(SmithyGoDependency.SMITHY_PRELUDE); + } writer.addUseImports(SmithyGoDependency.SMITHY); var symbol = ctx.symbolProvider().toSymbol(shape); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java index 645e4d5b0..c1eed2684 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java @@ -10,6 +10,7 @@ import software.amazon.smithy.go.codegen.Writable; import software.amazon.smithy.go.codegen.knowledge.GoPointableIndex; import software.amazon.smithy.go.codegen.util.ShapeUtil; +import software.amazon.smithy.model.loader.Prelude; import software.amazon.smithy.model.shapes.MemberShape; import software.amazon.smithy.model.shapes.Shape; import software.amazon.smithy.model.shapes.StructureShape; @@ -32,6 +33,9 @@ public StructureSerializer(GoCodegenContext ctx, StructureShape shape) { @Override public void accept(GoWriter writer) { writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); + if (Prelude.isPublicPreludeShape(shape)) { + writer.addUseImports(SmithyGoDependency.SMITHY_PRELUDE); + } var symbol = ctx.symbolProvider().toSymbol(shape); var members = shape.members().stream() diff --git a/eventstream/types.go b/eventstream/types.go new file mode 100644 index 000000000..4627bb209 --- /dev/null +++ b/eventstream/types.go @@ -0,0 +1,26 @@ +package eventstream + +import "github.com/aws/smithy-go" + +// UnknownUnionMember is returned when a union member is returned over the +// wire, but has an unknown tag. +type UnknownUnionMember struct { + Tag string + Value []byte +} + +// Deserialize is a no-op. The raw bytes are already captured in Value. +func (*UnknownUnionMember) Deserialize(smithy.ShapeDeserializer) error { + return nil +} + +// UnknownMessageError provides an error when a message is received from the +// stream, but the reader is unable to determine what kind of message it is. +type UnknownMessageError struct { + Type string + Message *Message +} + +func (e *UnknownMessageError) Error() string { + return "unknown event stream message type, " + e.Type +} diff --git a/eventstream/codec.go b/internal/eventstream/codec.go similarity index 58% rename from eventstream/codec.go rename to internal/eventstream/codec.go index 548e6f9ca..f18e15782 100644 --- a/eventstream/codec.go +++ b/internal/eventstream/codec.go @@ -6,58 +6,58 @@ import ( "io" "github.com/aws/smithy-go" + "github.com/aws/smithy-go/eventstream" ) // Codec orchestrates event stream message serde for protocols that use the -// standard event stream binary framing. All existing AWS protocols (and the -// Smithy rpcv2Cbor protocol) use this framing. +// standard event stream binary framing. type Codec struct { - Codec smithy.Codec - ContentType string + Serializer func() smithy.ShapeSerializer + Deserializer func([]byte) smithy.ShapeDeserializer + ContentType string - encoder *Encoder - decoder *Decoder + encoder *eventstream.Encoder + decoder *eventstream.Decoder payloadBuf []byte } -func (c *Codec) enc() *Encoder { +func (c *Codec) enc() *eventstream.Encoder { if c.encoder == nil { - c.encoder = NewEncoder() + c.encoder = eventstream.NewEncoder() } return c.encoder } -func (c *Codec) dec() *Decoder { +func (c *Codec) dec() *eventstream.Decoder { if c.decoder == nil { - c.decoder = NewDecoder() + c.decoder = eventstream.NewDecoder() } return c.decoder } // SerializeEventMessage serializes an event to the input stream. func (c *Codec) SerializeEventMessage(schema, variant *smithy.Schema, v smithy.Serializable, w io.Writer) error { - var msg Message + var msg eventstream.Message - inner := c.Codec.Serializer() - ss := NewShapeSerializer(&msg, inner) + inner := c.Serializer() + ss := eventstream.NewShapeSerializer(&msg, inner) v.Serialize(ss) - msg.Headers.Set(MessageTypeHeader, StringValue(EventMessageType)) - msg.Headers.Set(EventTypeHeader, StringValue(variant.MemberName())) + msg.Headers.Set(eventstream.MessageTypeHeader, eventstream.StringValue(eventstream.EventMessageType)) + msg.Headers.Set(eventstream.EventTypeHeader, eventstream.StringValue(variant.MemberName())) if ct := ss.ContentType(); ct != "" { - msg.Headers.Set(ContentTypeHeader, StringValue(ct)) + msg.Headers.Set(eventstream.ContentTypeHeader, eventstream.StringValue(ct)) } else if payload := inner.Bytes(); len(payload) > 0 { msg.Payload = payload - msg.Headers.Set(ContentTypeHeader, StringValue(c.ContentType)) + msg.Headers.Set(eventstream.ContentTypeHeader, eventstream.StringValue(c.ContentType)) } return c.enc().Encode(w, msg) } -// DeserializeEventMessage reads an event from the output stream. Returns -// io.EOF when the stream is complete. +// DeserializeEventMessage reads an event from the output stream. func (c *Codec) DeserializeEventMessage(schema *smithy.Schema, types *smithy.TypeRegistry, r io.Reader) (smithy.Deserializable, error) { for { c.payloadBuf = c.payloadBuf[0:0] @@ -69,25 +69,25 @@ func (c *Codec) DeserializeEventMessage(schema *smithy.Schema, types *smithy.Typ return nil, fmt.Errorf("decode event: %w", err) } - msgType := msg.Headers.Get(MessageTypeHeader) + msgType := msg.Headers.Get(eventstream.MessageTypeHeader) if msgType == nil { - return nil, fmt.Errorf("missing %s header", MessageTypeHeader) + return nil, fmt.Errorf("missing %s header", eventstream.MessageTypeHeader) } switch msgType.String() { - case EventMessageType: + case eventstream.EventMessageType: event, err := c.deserializeEvent(schema, types, &msg) if err != nil { return nil, err } return event, nil - case ExceptionMessageType: + case eventstream.ExceptionMessageType: return nil, c.deserializeException(schema, types, &msg) - case ErrorMessageType: + case eventstream.ErrorMessageType: return nil, deserializeError(&msg) default: mc := msg.Clone() - return nil, &UnknownMessageError{ + return nil, &eventstream.UnknownMessageError{ Type: msgType.String(), Message: &mc, } @@ -95,10 +95,10 @@ func (c *Codec) DeserializeEventMessage(schema *smithy.Schema, types *smithy.Typ } } -func (c *Codec) deserializeEvent(schema *smithy.Schema, types *smithy.TypeRegistry, msg *Message) (smithy.Deserializable, error) { - eventType := msg.Headers.Get(EventTypeHeader) +func (c *Codec) deserializeEvent(schema *smithy.Schema, types *smithy.TypeRegistry, msg *eventstream.Message) (smithy.Deserializable, error) { + eventType := msg.Headers.Get(eventstream.EventTypeHeader) if eventType == nil { - return nil, fmt.Errorf("missing %s header", EventTypeHeader) + return nil, fmt.Errorf("missing %s header", eventstream.EventTypeHeader) } member := schema.Member(eventType.String()) @@ -116,8 +116,8 @@ func (c *Codec) deserializeEvent(schema *smithy.Schema, types *smithy.TypeRegist return nil, fmt.Errorf("event type %s is not deserializable", eventType.String()) } - inner := c.Codec.Deserializer(msg.Payload) - ed := NewShapeDeserializer(msg, inner) + inner := c.Deserializer(msg.Payload) + ed := eventstream.NewShapeDeserializer(msg, inner) if err := instance.Deserialize(ed); err != nil { return nil, fmt.Errorf("deserialize event %s: %w", eventType.String(), err) } @@ -125,16 +125,16 @@ func (c *Codec) deserializeEvent(schema *smithy.Schema, types *smithy.TypeRegist return instance, nil } -func (c *Codec) unknownEvent(tag string, msg *Message) (*UnknownUnionMember, error) { +func (c *Codec) unknownEvent(tag string, msg *eventstream.Message) (*eventstream.UnknownUnionMember, error) { var buf bytes.Buffer c.enc().Encode(&buf, *msg) - return &UnknownUnionMember{Tag: tag, Value: buf.Bytes()}, nil + return &eventstream.UnknownUnionMember{Tag: tag, Value: buf.Bytes()}, nil } -func (c *Codec) deserializeException(schema *smithy.Schema, types *smithy.TypeRegistry, msg *Message) error { - exType := msg.Headers.Get(ExceptionTypeHeader) +func (c *Codec) deserializeException(schema *smithy.Schema, types *smithy.TypeRegistry, msg *eventstream.Message) error { + exType := msg.Headers.Get(eventstream.ExceptionTypeHeader) if exType == nil { - return fmt.Errorf("missing %s header", ExceptionTypeHeader) + return fmt.Errorf("missing %s header", eventstream.ExceptionTypeHeader) } var id string @@ -152,8 +152,8 @@ func (c *Codec) deserializeException(schema *smithy.Schema, types *smithy.TypeRe } } - inner := c.Codec.Deserializer(msg.Payload) - ed := NewShapeDeserializer(msg, inner) + inner := c.Deserializer(msg.Payload) + ed := eventstream.NewShapeDeserializer(msg, inner) if err := perr.Deserialize(ed); err != nil { return fmt.Errorf("deserialize exception %s: %w", exType.String(), err) } @@ -164,15 +164,15 @@ func (c *Codec) deserializeException(schema *smithy.Schema, types *smithy.TypeRe // SerializeInitialRequest serializes the operation input as the first event // stream message with :event-type "initial-request". func (c *Codec) SerializeInitialRequest(schema *smithy.Schema, v smithy.Serializable, w io.Writer) error { - ss := c.Codec.Serializer() + ss := c.Serializer() v.Serialize(ss) - var msg Message - msg.Headers.Set(MessageTypeHeader, StringValue(EventMessageType)) - msg.Headers.Set(EventTypeHeader, StringValue("initial-request")) + var msg eventstream.Message + msg.Headers.Set(eventstream.MessageTypeHeader, eventstream.StringValue(eventstream.EventMessageType)) + msg.Headers.Set(eventstream.EventTypeHeader, eventstream.StringValue("initial-request")) if payload := ss.Bytes(); len(payload) > 0 { msg.Payload = payload - msg.Headers.Set(ContentTypeHeader, StringValue(c.ContentType)) + msg.Headers.Set(eventstream.ContentTypeHeader, eventstream.StringValue(c.ContentType)) } return c.enc().Encode(w, msg) @@ -187,13 +187,13 @@ func (c *Codec) DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, o return fmt.Errorf("decode initial response: %w", err) } - eventType := msg.Headers.Get(EventTypeHeader) + eventType := msg.Headers.Get(eventstream.EventTypeHeader) if eventType == nil || eventType.String() != "initial-response" { return fmt.Errorf("expected initial-response, got %v", eventType) } if len(msg.Payload) > 0 { - sd := c.Codec.Deserializer(msg.Payload) + sd := c.Deserializer(msg.Payload) if err := out.Deserialize(sd); err != nil { return fmt.Errorf("deserialize initial response: %w", err) } @@ -202,36 +202,13 @@ func (c *Codec) DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, o return nil } -// UnknownUnionMember is returned when a union member is returned over the -// wire, but has an unknown tag. -type UnknownUnionMember struct { - Tag string - Value []byte -} - -// Deserialize is a no-op. The raw bytes are already captured in Value. -func (*UnknownUnionMember) Deserialize(smithy.ShapeDeserializer) error { - return nil -} - -// UnknownMessageError provides an error when a message is received from the -// stream, but the reader is unable to determine what kind of message it is. -type UnknownMessageError struct { - Type string - Message *Message -} - -func (e *UnknownMessageError) Error() string { - return "unknown event stream message type, " + e.Type -} - -func deserializeError(msg *Message) error { +func deserializeError(msg *eventstream.Message) error { code := "UnknownError" message := code - if v := msg.Headers.Get(ErrorCodeHeader); v != nil { + if v := msg.Headers.Get(eventstream.ErrorCodeHeader); v != nil { code = v.String() } - if v := msg.Headers.Get(ErrorMessageHeader); v != nil { + if v := msg.Headers.Get(eventstream.ErrorMessageHeader); v != nil { message = v.String() } return &smithy.GenericAPIError{ diff --git a/eventstream/no_event_stream.go b/internal/eventstream/no_event_stream.go similarity index 100% rename from eventstream/no_event_stream.go rename to internal/eventstream/no_event_stream.go diff --git a/internal/serde/stack.go b/internal/serde/stack.go new file mode 100644 index 000000000..d5f88333f --- /dev/null +++ b/internal/serde/stack.go @@ -0,0 +1,44 @@ +package serde + +// Stack is a generic slice-backed stack pre-allocated to avoid growth +// allocations for typical serde nesting depths. +type Stack[T any] struct { + values []T +} + +// NewStack returns a Stack pre-allocated with capacity 8. +func NewStack[T any]() Stack[T] { + return Stack[T]{values: make([]T, 0, 8)} +} + +// Push adds a value to the top of the stack. +func (s *Stack[T]) Push(v T) { + s.values = append(s.values, v) +} + +// Pop removes and returns the top value. +func (s *Stack[T]) Pop() T { + v := s.values[len(s.values)-1] + var zero T + s.values[len(s.values)-1] = zero + s.values = s.values[:len(s.values)-1] + return v +} + +// Top returns a pointer to the top value without removing it. +func (s *Stack[T]) Top() *T { + if len(s.values) == 0 { + return nil + } + return &s.values[len(s.values)-1] +} + +// Len returns the number of elements in the stack. +func (s *Stack[T]) Len() int { + return len(s.values) +} + +// Values returns the underlying slice for indexed access. +func (s *Stack[T]) Values() []T { + return s.values +} diff --git a/middleware/context.go b/middleware/context.go index f4b03ab37..f51aa4f04 100644 --- a/middleware/context.go +++ b/middleware/context.go @@ -5,7 +5,6 @@ import "context" type ( serviceIDKey struct{} operationNameKey struct{} - serviceNameKey struct{} ) // WithServiceID adds a service ID to the context, scoped to middleware stack @@ -40,19 +39,3 @@ func GetOperationName(ctx context.Context) string { name, _ := GetStackValue(ctx, operationNameKey{}).(string) return name } - -// WithServiceName adds the service name to the context, scoped to middleware -// stack values. -// -// This API is called in the client runtime when bootstrapping an operation and -// should not typically be used directly. -func WithServiceName(parent context.Context, id string) context.Context { - return WithStackValue(parent, serviceNameKey{}, id) -} - -// GetServiceName retrieves the service name from the context. This is -// ALWAYS the service shape's name from its Smithy model. -func GetServiceName(ctx context.Context) string { - name, _ := GetStackValue(ctx, serviceNameKey{}).(string) - return name -} diff --git a/schema.go b/schema.go index da3c2763a..87fbe20e7 100644 --- a/schema.go +++ b/schema.go @@ -4,8 +4,6 @@ import ( "fmt" "maps" "strings" - - "github.com/aws/smithy-go/traits" ) // ShapeType is a type of Smithy shape. @@ -66,10 +64,10 @@ func stoid(s string) ShapeID { type Schema struct { id ShapeID typ ShapeType - members map[string]*Schema // member name -> schema - traits map[string]Trait // trait ID -> trait (effective view; for members, target's traits merged with member's overrides) - directTraits map[string]Trait // trait ID -> trait (member schemas only: only traits declared directly on the member) - targetID ShapeID // for member schemas, the target's shape ID + members map[string]*Schema // member name -> schema + traits map[ShapeID]Trait // trait ID -> trait (effective view; for members, target's traits merged with member's overrides) + directTraits map[ShapeID]Trait // trait ID -> trait (member schemas only: only traits declared directly on the member) + targetID ShapeID // for member schemas, the target's shape ID listMember *Schema mapKey, mapValue *Schema @@ -77,7 +75,7 @@ type Schema struct { // NewSchema creates a new Schema with the given shape ID and traits. func NewSchema(id ShapeID, typ ShapeType, numMembers int, traits ...Trait) *Schema { - traitMap := make(map[string]Trait, len(traits)) + traitMap := make(map[ShapeID]Trait, len(traits)) for _, t := range traits { traitMap[t.TraitID()] = t } @@ -98,14 +96,14 @@ func NewSchema(id ShapeID, typ ShapeType, numMembers int, traits ...Trait) *Sche // member's direct trait view (accessed via [SchemaDirectTrait]) contains // only the overrides, i.e. the traits declared directly on the member. func (s *Schema) AddMember(name string, target *Schema, traits ...Trait) *Schema { - directs := make(map[string]Trait, len(traits)) + directs := make(map[ShapeID]Trait, len(traits)) for _, t := range traits { directs[t.TraitID()] = t } merged := maps.Clone(target.traits) if merged == nil { - merged = map[string]Trait{} + merged = map[ShapeID]Trait{} } for id, t := range directs { merged[id] = t @@ -210,6 +208,17 @@ func (s *OperationSchema) IsOutputEventStream() bool { return s.outputStream } +// ServiceSchema describes a service shape. +type ServiceSchema struct { + *Schema + Version string +} + +// NewServiceSchema returns a ServiceSchema for the given service shape. +func NewServiceSchema(schema *Schema, version string) *ServiceSchema { + return &ServiceSchema{Schema: schema, Version: version} +} + // SchemaTrait returns the target trait on the schema if it exists. // // For member schemas this returns the effective trait, which is the trait @@ -263,7 +272,7 @@ func isEventStream(s *Schema) bool { if m.typ != ShapeTypeUnion { continue } - if _, ok := SchemaTrait[*traits.Streaming](m); ok { + if _, ok := m.traits[ShapeID{Namespace: "smithy.api", Name: "streaming"}]; ok { return true } } diff --git a/serde.go b/serde.go index a23e6f09c..59468b2b6 100644 --- a/serde.go +++ b/serde.go @@ -9,13 +9,6 @@ import ( "github.com/aws/smithy-go/document" ) -// Codec provides implementations of Serializer and ShapeDeserializer to be -// used by a Protocol. -type Codec interface { - Serializer() ShapeSerializer - Deserializer([]byte) ShapeDeserializer -} - // ShapeSerializer implements the marshaling of an in-code representation of a // shape to an unspecified data format, which is determined by the // implementation. @@ -193,19 +186,14 @@ func ReadStruct(d ShapeDeserializer, schema *Schema, memberFn func(*Schema) erro for { ms, err := d.ReadStructMember() - if ms == nil { - return nil - } - - // TODO(serde2): err check should be first. ran into this with XML - // because of how that shape deserializer operates on innerXML, you get - // a guaranteed EOF in the end, but I have explicitly swallowed that - // EOF there when it's on the outer struct since that's how that - // deserializer has to work if err != nil { return err } + if ms == nil { + return nil + } + if err := memberFn(ms); err != nil { return err } diff --git a/smithy-http-protocols/internal/cbor/codec.go b/smithy-http-protocols/internal/cbor/codec.go deleted file mode 100644 index d07c6c087..000000000 --- a/smithy-http-protocols/internal/cbor/codec.go +++ /dev/null @@ -1,20 +0,0 @@ -package cbor - -import ( - "github.com/aws/smithy-go" -) - -// Codec is a CBOR codec. -type Codec struct{} - -var _ smithy.Codec = (*Codec)(nil) - -// Serializer returns a CBOR shape serializer. -func (c *Codec) Serializer() smithy.ShapeSerializer { - return NewShapeSerializer() -} - -// Deserializer returns a CBOR shape deserializer. -func (c *Codec) Deserializer(p []byte) smithy.ShapeDeserializer { - return NewShapeDeserializer(p) -} diff --git a/smithy-http-protocols/internal/cbor/shape_deserializer.go b/smithy-http-protocols/internal/cbor/shape_deserializer.go index 4c08a6090..4a4a49382 100644 --- a/smithy-http-protocols/internal/cbor/shape_deserializer.go +++ b/smithy-http-protocols/internal/cbor/shape_deserializer.go @@ -10,6 +10,7 @@ import ( "github.com/aws/smithy-go" "github.com/aws/smithy-go/document" smithycbor "github.com/aws/smithy-go/encoding/cbor" + "github.com/aws/smithy-go/internal/serde" ) var errUnexpectedEOF = errors.New("unexpected end of CBOR data") @@ -18,7 +19,7 @@ var errUnexpectedEOF = errors.New("unexpected end of CBOR data") type ShapeDeserializer struct { p []byte off int - head deserStack + head serde.Stack[deserCtx] opts ShapeDeserializerOptions } @@ -37,25 +38,6 @@ type deserCtx struct { remaining int } -type deserStack struct { - values []deserCtx -} - -func (s *deserStack) top() *deserCtx { - if len(s.values) == 0 { - return nil - } - return &s.values[len(s.values)-1] -} - -func (s *deserStack) push(v deserCtx) { - s.values = append(s.values, v) -} - -func (s *deserStack) pop() { - s.values = s.values[:len(s.values)-1] -} - // ShapeDeserializerOptions configures ShapeDeserializer. type ShapeDeserializerOptions struct{} @@ -67,7 +49,7 @@ func NewShapeDeserializer(p []byte, opts ...func(*ShapeDeserializerOptions)) *Sh for _, fn := range opts { fn(&o) } - return &ShapeDeserializer{p: p, opts: o} + return &ShapeDeserializer{p: p, head: serde.NewStack[deserCtx](), opts: o} } func (d *ShapeDeserializer) eof() bool { @@ -433,20 +415,20 @@ func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { } if d.peekMinor() == minorIndefinite { d.off++ - d.head.push(deserCtx{kind: deserCtxList, remaining: -1}) + d.head.Push(deserCtx{kind: deserCtxList, remaining: -1}) return nil } count, err := d.readArg() if err != nil { return err } - d.head.push(deserCtx{kind: deserCtxList, remaining: int(count)}) + d.head.Push(deserCtx{kind: deserCtxList, remaining: int(count)}) return nil } // ReadListItem implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadListItem(s *smithy.Schema) (bool, error) { - lc := d.head.top() + lc := d.head.Top() if lc == nil || lc.kind != deserCtxList { return false, fmt.Errorf("ReadListItem called without ReadList") } @@ -454,13 +436,13 @@ func (d *ShapeDeserializer) ReadListItem(s *smithy.Schema) (bool, error) { if lc.remaining == -1 { if d.off < len(d.p) && d.p[d.off] == 0xff { d.off++ - d.head.pop() + d.head.Pop() return false, nil } return true, nil } if lc.remaining <= 0 { - d.head.pop() + d.head.Pop() return false, nil } lc.remaining-- @@ -478,20 +460,20 @@ func (d *ShapeDeserializer) ReadMap(s *smithy.Schema) error { } if d.peekMinor() == minorIndefinite { d.off++ - d.head.push(deserCtx{kind: deserCtxMap, remaining: -1}) + d.head.Push(deserCtx{kind: deserCtxMap, remaining: -1}) return nil } count, err := d.readArg() if err != nil { return err } - d.head.push(deserCtx{kind: deserCtxMap, remaining: int(count)}) + d.head.Push(deserCtx{kind: deserCtxMap, remaining: int(count)}) return nil } // ReadMapKey implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadMapKey(s *smithy.Schema) (string, bool, error) { - mc := d.head.top() + mc := d.head.Top() if mc == nil || mc.kind != deserCtxMap { return "", false, errors.New("ReadMapKey called without ReadMap") } @@ -499,12 +481,12 @@ func (d *ShapeDeserializer) ReadMapKey(s *smithy.Schema) (string, bool, error) { if mc.remaining == -1 { if d.off < len(d.p) && d.p[d.off] == 0xff { d.off++ - d.head.pop() + d.head.Pop() return "", false, nil } } else { if mc.remaining <= 0 { - d.head.pop() + d.head.Pop() return "", false, nil } mc.remaining-- @@ -528,20 +510,20 @@ func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error { } if d.peekMinor() == minorIndefinite { d.off++ - d.head.push(deserCtx{kind: deserCtxStruct, schema: s, remaining: -1}) + d.head.Push(deserCtx{kind: deserCtxStruct, schema: s, remaining: -1}) return nil } count, err := d.readArg() if err != nil { return err } - d.head.push(deserCtx{kind: deserCtxStruct, schema: s, remaining: int(count)}) + d.head.Push(deserCtx{kind: deserCtxStruct, schema: s, remaining: int(count)}) return nil } // ReadStructMember implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { - sc := d.head.top() + sc := d.head.Top() if sc == nil || sc.kind != deserCtxStruct { return nil, fmt.Errorf("ReadStructMember called without ReadStruct") } @@ -549,12 +531,12 @@ func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { if sc.remaining == -1 { if d.off < len(d.p) && d.p[d.off] == 0xff { d.off++ - d.head.pop() + d.head.Pop() return nil, nil } } else { if sc.remaining <= 0 { - d.head.pop() + d.head.Pop() return nil, nil } sc.remaining-- @@ -578,7 +560,7 @@ func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { // ReadUnion implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) { - top := d.head.top() + top := d.head.Top() if top == nil || top.kind != deserCtxUnion { // first call: open the map if d.eof() { return nil, errUnexpectedEOF @@ -588,26 +570,26 @@ func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) } if d.peekMinor() == minorIndefinite { d.off++ - d.head.push(deserCtx{kind: deserCtxUnion, schema: s, remaining: -1}) + d.head.Push(deserCtx{kind: deserCtxUnion, schema: s, remaining: -1}) } else { count, err := d.readArg() if err != nil { return nil, err } - d.head.push(deserCtx{kind: deserCtxUnion, schema: s, remaining: int(count)}) + d.head.Push(deserCtx{kind: deserCtxUnion, schema: s, remaining: int(count)}) } } - uc := d.head.top() + uc := d.head.Top() for { if uc.remaining == -1 { if d.off < len(d.p) && d.p[d.off] == 0xff { d.off++ - d.head.pop() + d.head.Pop() return nil, nil } } else if uc.remaining <= 0 { - d.head.pop() + d.head.Pop() return nil, nil } else { uc.remaining-- diff --git a/smithy-http-protocols/rpcv2/rpcv2.go b/smithy-http-protocols/rpcv2/rpcv2.go index ca9f68b64..f8abbe86d 100644 --- a/smithy-http-protocols/rpcv2/rpcv2.go +++ b/smithy-http-protocols/rpcv2/rpcv2.go @@ -8,11 +8,12 @@ import ( "net/http" "github.com/aws/smithy-go" - "github.com/aws/smithy-go/eventstream" + internales "github.com/aws/smithy-go/internal/eventstream" internalerrors "github.com/aws/smithy-go/internal/errors" smithyio "github.com/aws/smithy-go/io" "github.com/aws/smithy-go/middleware" internalcbor "github.com/aws/smithy-go/smithy-http-protocols/internal/cbor" + "github.com/aws/smithy-go/traits" smithyhttp "github.com/aws/smithy-go/transport/http" ) @@ -21,53 +22,38 @@ import ( // RPCv2 protocol family: // - CBOR: https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html type Protocol struct { - options ProtocolOptions + queryCompatible bool + serviceName string - codec smithy.Codec - contentType string - protocolID string - - *eventstream.Codec -} - -// ProtocolOptions configures a Protocol. -type ProtocolOptions struct { - // When enabled, support reading legacy AWS query error codes in error - // responses. - // - // See https://smithy.io/2.0/aws/protocols/aws-query-protocol.html#aws-protocols-awsquerycompatible-trait. - UseQueryCompatible bool -} - -// UseQueryCompatible enables support for AWS query compatibility. -func UseQueryCompatible(o *ProtocolOptions) { - o.UseQueryCompatible = true + *internales.Codec } var _ smithyhttp.ClientProtocol = (*Protocol)(nil) +// ProtocolOptions configures smithy.protocols#rpcv2Cbor. +type ProtocolOptions struct{} + // NewCBOR returns an instance of the smithy.protocols#rpcv2Cbor protocol. -func NewCBOR(opts ...func(*ProtocolOptions)) *Protocol { - var options ProtocolOptions - for _, opt := range opts { - opt(&options) +func NewCBOR(service *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Protocol { + var o ProtocolOptions + for _, fn := range opts { + fn(&o) } - codec := &internalcbor.Codec{} + _, qc := smithy.SchemaTrait[*traits.AWSQueryCompatible](service.Schema) return &Protocol{ - options: options, - codec: codec, - contentType: "application/cbor", - protocolID: "smithy.protocols#rpcv2Cbor", - Codec: &eventstream.Codec{ - Codec: codec, - ContentType: "application/cbor", + queryCompatible: qc, + serviceName: service.Schema.ID().Name, + Codec: &internales.Codec{ + Serializer: func() smithy.ShapeSerializer { return internalcbor.NewShapeSerializer() }, + Deserializer: func(p []byte) smithy.ShapeDeserializer { return internalcbor.NewShapeDeserializer(p) }, + ContentType: "application/cbor", }, } } // ID identifies the protocol. -func (p *Protocol) ID() string { - return p.protocolID +func (p *Protocol) ID() smithy.ShapeID { + return smithy.ShapeID{Namespace: "smithy.protocols", Name: "rpcv2Cbor"} } // SerializeRequest serializes a request for rpcv2Cbor. @@ -79,10 +65,10 @@ func (p *Protocol) SerializeRequest( ) error { req.Method = http.MethodPost req.URL.Path = fmt.Sprintf("/service/%s/operation/%s", - middleware.GetServiceName(ctx), middleware.GetOperationName(ctx)) + p.serviceName, middleware.GetOperationName(ctx)) req.Header.Set("Smithy-Protocol", "rpc-v2-cbor") req.Header.Set("Accept", "application/cbor") - if p.options.UseQueryCompatible { + if p.queryCompatible { req.Header.Set("X-Amzn-Query-Mode", "true") } @@ -91,7 +77,7 @@ func (p *Protocol) SerializeRequest( return nil } - ss := p.codec.Serializer() + ss := internalcbor.NewShapeSerializer() in.Serialize(ss) payload := ss.Bytes() @@ -99,13 +85,11 @@ func (p *Protocol) SerializeRequest( return nil } - // operations targeting Unit MUST NOT have a body, check if we backfilled - // an input - if cs, ok := ss.(*internalcbor.ShapeSerializer); ok && cs.IsUnitShape() { + if ss.IsUnitShape() { return nil } - req.Header.Set("Content-Type", p.contentType) + req.Header.Set("Content-Type", "application/cbor") sreq, err := req.SetStream(bytes.NewReader(payload)) if err != nil { @@ -141,7 +125,7 @@ func (p *Protocol) DeserializeResponse( return nil } - sd := p.codec.Deserializer(payload) + sd := internalcbor.NewShapeDeserializer(payload) if err := out.Deserialize(sd); err != nil { return &smithy.DeserializationError{Err: err} } @@ -199,7 +183,7 @@ func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithy var queryCode string var queryFault smithy.ErrorFault - if p.options.UseQueryCompatible { + if p.queryCompatible { queryHeader := response.Header.Get("X-Amzn-Query-Error") queryCode, queryFault = internalerrors.ParseQueryError(queryHeader) } @@ -218,7 +202,7 @@ func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithy } if len(bodyBytes) > 0 { - deser := p.codec.Deserializer(bodyBytes) + deser := internalcbor.NewShapeDeserializer(bodyBytes) if err := perr.Deserialize(deser); err != nil { return &smithy.DeserializationError{Err: err} } diff --git a/trait.go b/trait.go index a6e8ca157..9a73dcf28 100644 --- a/trait.go +++ b/trait.go @@ -4,9 +4,5 @@ package smithy // related to (de)serialization are included in code-generated Schemas for the // client. type Trait interface { - TraitID() string // TODO(serde2): should return a ShapeID + TraitID() ShapeID } - -// TODO(serde2): investigate performance tradeoff of using an "indexed" map for -// the known traits (basically the ones defined here) since the rest- and -// xml-based protocols do a ton of trait lookup (which translates to map lookup) diff --git a/traits/http.go b/traits/http.go index 2575519d2..b06e9fed1 100644 --- a/traits/http.go +++ b/traits/http.go @@ -1,24 +1,26 @@ package traits +import smithy "github.com/aws/smithy-go" + // HTTPHeader represents smithy.api#httpHeader. type HTTPHeader struct { Name string } // TraitID identifies the trait. -func (*HTTPHeader) TraitID() string { return "smithy.api#httpHeader" } +func (*HTTPHeader) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpHeader"} } // HTTPLabel represents smithy.api#httpLabel. type HTTPLabel struct{} // TraitID identifies the trait. -func (*HTTPLabel) TraitID() string { return "smithy.api#httpLabel" } +func (*HTTPLabel) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpLabel"} } // HTTPPayload represents smithy.api#httpPayload. type HTTPPayload struct{} // TraitID identifies the trait. -func (*HTTPPayload) TraitID() string { return "smithy.api#httpPayload" } +func (*HTTPPayload) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpPayload"} } // HTTPPrefixHeaders represents smithy.api#httpPrefixHeaders. type HTTPPrefixHeaders struct { @@ -26,7 +28,7 @@ type HTTPPrefixHeaders struct { } // TraitID identifies the trait. -func (*HTTPPrefixHeaders) TraitID() string { return "smithy.api#httpPrefixHeaders" } +func (*HTTPPrefixHeaders) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpPrefixHeaders"} } // HTTPQuery represents smithy.api#httpQuery. type HTTPQuery struct { @@ -34,19 +36,19 @@ type HTTPQuery struct { } // TraitID identifies the trait. -func (*HTTPQuery) TraitID() string { return "smithy.api#httpQuery" } +func (*HTTPQuery) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpQuery"} } // HTTPQueryParams represents smithy.api#httpQueryParams. type HTTPQueryParams struct{} // TraitID identifies the trait. -func (*HTTPQueryParams) TraitID() string { return "smithy.api#httpQueryParams" } +func (*HTTPQueryParams) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpQueryParams"} } // HTTPResponseCode represents smithy.api#httpResponseCode. type HTTPResponseCode struct{} // TraitID identifies the trait. -func (*HTTPResponseCode) TraitID() string { return "smithy.api#httpResponseCode" } +func (*HTTPResponseCode) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpResponseCode"} } // HTTP represents smithy.api#http. type HTTP struct { @@ -56,7 +58,7 @@ type HTTP struct { } // TraitID identifies the trait. -func (*HTTP) TraitID() string { return "smithy.api#http" } +func (*HTTP) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "http"} } // HTTPError represents smithy.api#httpError. type HTTPError struct { @@ -64,4 +66,4 @@ type HTTPError struct { } // TraitID identifies the trait. -func (*HTTPError) TraitID() string { return "smithy.api#httpError" } +func (*HTTPError) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "httpError"} } diff --git a/traits/serde.go b/traits/serde.go index d4a2c0441..25b7f0dd3 100644 --- a/traits/serde.go +++ b/traits/serde.go @@ -1,12 +1,14 @@ package traits +import smithy "github.com/aws/smithy-go" + // JSONName represents smithy.api#jsonName. type JSONName struct { Name string } // TraitID identifies the trait. -func (*JSONName) TraitID() string { return "smithy.api#jsonName" } +func (*JSONName) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "jsonName"} } // MediaType represents smithy.api#mediaType. type MediaType struct { @@ -14,7 +16,7 @@ type MediaType struct { } // TraitID identifies the trait. -func (*MediaType) TraitID() string { return "smithy.api#mediaType" } +func (*MediaType) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "mediaType"} } // TimestampFormat represents smithy.api#timestampFormat. type TimestampFormat struct { @@ -22,19 +24,19 @@ type TimestampFormat struct { } // TraitID identifies the trait. -func (*TimestampFormat) TraitID() string { return "smithy.api#timestampFormat" } +func (*TimestampFormat) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "timestampFormat"} } // XMLAttribute represents smithy.api#xmlAttribute. type XMLAttribute struct{} // TraitID identifies the trait. -func (*XMLAttribute) TraitID() string { return "smithy.api#xmlAttribute" } +func (*XMLAttribute) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "xmlAttribute"} } // XMLFlattened represents smithy.api#xmlFlattened. type XMLFlattened struct{} // TraitID identifies the trait. -func (*XMLFlattened) TraitID() string { return "smithy.api#xmlFlattened" } +func (*XMLFlattened) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "xmlFlattened"} } // XMLName represents smithy.api#xmlName. type XMLName struct { @@ -42,7 +44,7 @@ type XMLName struct { } // TraitID identifies the trait. -func (*XMLName) TraitID() string { return "smithy.api#xmlName" } +func (*XMLName) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "xmlName"} } // XMLNamespace represents smithy.api#xmlNamespace. type XMLNamespace struct { @@ -51,4 +53,4 @@ type XMLNamespace struct { } // TraitID identifies the trait. -func (*XMLNamespace) TraitID() string { return "smithy.api#xmlNamespace" } +func (*XMLNamespace) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "xmlNamespace"} } diff --git a/traits/traits.go b/traits/traits.go index 932bf5ba2..599be4e54 100644 --- a/traits/traits.go +++ b/traits/traits.go @@ -2,41 +2,43 @@ // code-generated schemas. package traits +import smithy "github.com/aws/smithy-go" + // Sensitive represents smithy.api#sensitive. type Sensitive struct{} // TraitID identifies the trait. -func (*Sensitive) TraitID() string { return "smithy.api#sensitive" } +func (*Sensitive) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "sensitive"} } // EventHeader represents smithy.api#eventHeader. type EventHeader struct{} // TraitID identifies the trait. -func (*EventHeader) TraitID() string { return "smithy.api#eventHeader" } +func (*EventHeader) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "eventHeader"} } // EventPayload represents smithy.api#eventPayload. type EventPayload struct{} // TraitID identifies the trait. -func (*EventPayload) TraitID() string { return "smithy.api#eventPayload" } +func (*EventPayload) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "eventPayload"} } // Streaming represents smithy.api#streaming. type Streaming struct{} // TraitID identifies the trait. -func (*Streaming) TraitID() string { return "smithy.api#streaming" } +func (*Streaming) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "streaming"} } // HostLabel represents smithy.api#hostLabel. type HostLabel struct{} // TraitID identifies the trait. -func (*HostLabel) TraitID() string { return "smithy.api#hostLabel" } +func (*HostLabel) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.api", Name: "hostLabel"} } // ContextParam represents smithy.rules#contextParam. type ContextParam struct{} // TraitID identifies the trait. -func (*ContextParam) TraitID() string { return "smithy.rules#contextParam" } +func (*ContextParam) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.rules", Name: "contextParam"} } // AWSQueryError represents aws.protocols#awsQueryError. type AWSQueryError struct { @@ -45,7 +47,7 @@ type AWSQueryError struct { } // TraitID identifies the trait. -func (*AWSQueryError) TraitID() string { return "aws.protocols#awsQueryError" } +func (*AWSQueryError) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "aws.protocols", Name: "awsQueryError"} } // EC2QueryName represents aws.protocols#ec2QueryName. type EC2QueryName struct { @@ -53,7 +55,13 @@ type EC2QueryName struct { } // TraitID identifies the trait. -func (*EC2QueryName) TraitID() string { return "aws.protocols#ec2QueryName" } +func (*EC2QueryName) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "aws.protocols", Name: "ec2QueryName"} } + +// AWSQueryCompatible represents aws.protocols#awsQueryCompatible. +type AWSQueryCompatible struct{} + +// TraitID identifies the trait. +func (*AWSQueryCompatible) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "aws.protocols", Name: "awsQueryCompatible"} } // UnitShape is a synthetic trait applied to input/output shapes that were // backfilled from Unit. It indicates the shape has no defined members and @@ -61,4 +69,4 @@ func (*EC2QueryName) TraitID() string { return "aws.protocols#ec2QueryName" } type UnitShape struct{} // TraitID identifies the trait. -func (*UnitShape) TraitID() string { return "smithy.go#unitShape" } +func (*UnitShape) TraitID() smithy.ShapeID { return smithy.ShapeID{Namespace: "smithy.go", Name: "unitShape"} } diff --git a/transport/http/protocol.go b/transport/http/protocol.go index b54531f7d..80fc9e6f9 100644 --- a/transport/http/protocol.go +++ b/transport/http/protocol.go @@ -7,9 +7,6 @@ import ( "github.com/aws/smithy-go" ) -// TODO(serde2): review API surface for all Protocol impls, their New()s, and -// the way they are configured (options vs codec options) - // ClientProtocol defines the interface through which client-side operation // request/responses are (de)serialized across the wire. // @@ -17,7 +14,7 @@ import ( // to do so. In practice, a generated client will utilize one of the predefined // protocols implemented as part of the Smithy client runtime. type ClientProtocol interface { - ID() string // TODO(serde2): this should return shape ID + ID() smithy.ShapeID SerializeRequest(context.Context, *smithy.OperationSchema, smithy.Serializable, *Request) error DeserializeResponse(ctx context.Context, schema *smithy.OperationSchema, types *smithy.TypeRegistry, resp *Response, out smithy.Deserializable) error From 194a4fd8a0c426f0b61a3b5fd30c62b3935ab5bf Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Thu, 14 May 2026 19:48:14 -0400 Subject: [PATCH 15/38] make traits indexable (#658) --- schema.go | 142 +++++++++++++++++++++++++++++------------------- trait.go | 13 +++++ traits/index.go | 107 ++++++++++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+), 56 deletions(-) create mode 100644 traits/index.go diff --git a/schema.go b/schema.go index 87fbe20e7..410e1542f 100644 --- a/schema.go +++ b/schema.go @@ -2,7 +2,6 @@ package smithy import ( "fmt" - "maps" "strings" ) @@ -62,29 +61,48 @@ func stoid(s string) ShapeID { // Generated clients use schemas at runtime to dynamically (de)serialize // request/responses. type Schema struct { - id ShapeID - typ ShapeType - members map[string]*Schema // member name -> schema - traits map[ShapeID]Trait // trait ID -> trait (effective view; for members, target's traits merged with member's overrides) - directTraits map[ShapeID]Trait // trait ID -> trait (member schemas only: only traits declared directly on the member) - targetID ShapeID // for member schemas, the target's shape ID + id ShapeID + typ ShapeType + members map[string]*Schema // member name -> schema + traits map[ShapeID]Trait // trait ID -> non-indexed traits only + indexed []Trait // indexed trait slots, sized to max index present + directMask uint64 // bitmask: bit i set means indexed[i] was declared directly on this schema + targetID ShapeID // for member schemas, the target's shape ID listMember *Schema mapKey, mapValue *Schema } // NewSchema creates a new Schema with the given shape ID and traits. -func NewSchema(id ShapeID, typ ShapeType, numMembers int, traits ...Trait) *Schema { - traitMap := make(map[ShapeID]Trait, len(traits)) - for _, t := range traits { - traitMap[t.TraitID()] = t - } - return &Schema{ +func NewSchema(id ShapeID, typ ShapeType, numMembers int, ts ...Trait) *Schema { + s := &Schema{ id: id, typ: typ, members: make(map[string]*Schema, numMembers), - traits: traitMap, } + for _, t := range ts { + s.addTrait(t, true) + } + return s +} + +func (s *Schema) addTrait(t Trait, direct bool) { + if it, ok := t.(IndexableTrait); ok { + idx := it.TraitIndex() + if idx >= len(s.indexed) { + s.indexed = append(s.indexed, make([]Trait, idx-len(s.indexed)+1)...) + } + s.indexed[idx] = t + if direct { + s.directMask |= 1 << uint(idx) + } + return + } + + if s.traits == nil { + s.traits = map[ShapeID]Trait{} + } + s.traits[t.TraitID()] = t } // AddMember adds a member to the schema derived from the target, with @@ -95,30 +113,23 @@ func NewSchema(id ShapeID, typ ShapeType, numMembers int, traits ...Trait) *Sche // inherits all of the target's traits, then applies the overrides. The // member's direct trait view (accessed via [SchemaDirectTrait]) contains // only the overrides, i.e. the traits declared directly on the member. -func (s *Schema) AddMember(name string, target *Schema, traits ...Trait) *Schema { - directs := make(map[ShapeID]Trait, len(traits)) - for _, t := range traits { - directs[t.TraitID()] = t - } - - merged := maps.Clone(target.traits) - if merged == nil { - merged = map[ShapeID]Trait{} - } - for id, t := range directs { - merged[id] = t +func (s *Schema) AddMember(name string, target *Schema, ts ...Trait) *Schema { + m := &Schema{ + id: ShapeID{Member: name}, + typ: target.typ, + members: target.members, + indexed: cloneIndexed(target.indexed), + traits: cloneTraits(target.traits), + directMask: 0, // inherited traits are not direct + targetID: target.id, + listMember: target.listMember, + mapKey: target.mapKey, + mapValue: target.mapValue, } - m := &Schema{ - id: ShapeID{Member: name}, - typ: target.typ, - members: target.members, - traits: merged, - directTraits: directs, - targetID: target.id, - listMember: target.listMember, - mapKey: target.mapKey, - mapValue: target.mapValue, + // member-declared traits override and are direct + for _, t := range ts { + m.addTrait(t, true) } s.members[name] = m @@ -133,6 +144,26 @@ func (s *Schema) AddMember(name string, target *Schema, traits ...Trait) *Schema return m } +func cloneIndexed(src []Trait) []Trait { + if src == nil { + return nil + } + dst := make([]Trait, len(src)) + copy(dst, src) + return dst +} + +func cloneTraits(src map[ShapeID]Trait) map[ShapeID]Trait { + if src == nil { + return nil + } + dst := make(map[ShapeID]Trait, len(src)) + for k, v := range src { + dst[k] = v + } + return dst +} + // ListMember returns the "member" schema for list types. func (s *Schema) ListMember() *Schema { return s.listMember @@ -225,19 +256,7 @@ func NewServiceSchema(schema *Schema, version string) *ServiceSchema { // declared directly on the member if present, else the trait inherited from // the target shape. func SchemaTrait[T Trait](s *Schema) (T, bool) { - var trait T - - if s == nil { - return trait, false - } - - opaque, ok := s.traits[trait.TraitID()] - if !ok { - return trait, false - } - - tt, ok := opaque.(T) - return tt, ok + return schemaTrait[T](s, false) } // SchemaDirectTrait returns the target trait on the schema if it was @@ -247,20 +266,31 @@ func SchemaTrait[T Trait](s *Schema) (T, bool) { // member itself, ignoring any trait inherited from the target shape. For // non-member schemas this is equivalent to [SchemaTrait]. func SchemaDirectTrait[T Trait](s *Schema) (T, bool) { - var trait T + return schemaTrait[T](s, true) +} + +func schemaTrait[T Trait](s *Schema, directOnly bool) (T, bool) { + var zero T if s == nil { - return trait, false + return zero, false } - source := s.directTraits - if source == nil { - source = s.traits + if it, ok := Trait(zero).(IndexableTrait); ok { + idx := it.TraitIndex() + if idx >= len(s.indexed) { + return zero, false + } + if directOnly && s.directMask&(1< Date: Fri, 15 May 2026 13:33:44 -0400 Subject: [PATCH 16/38] fix synthetic namespace collide --- .../smithy/go/codegen/CodegenVisitor.java | 50 ++++++++++++++++--- .../smithy/go/codegen/OperationGenerator.java | 6 ++- .../smithy/go/codegen/SchemaGenerator.java | 18 +++---- .../smithy/go/codegen/TypeRegistry.java | 29 +++++++++-- schema.go | 3 ++ 5 files changed, 81 insertions(+), 25 deletions(-) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java index 4bf88b23b..904c5deb4 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java @@ -90,6 +90,7 @@ final class CodegenVisitor extends ShapeVisitor.Default { private final ProtocolDocumentGenerator protocolDocumentGenerator; private final EventStreamGenerator eventStreamGenerator; private final GoCodegenContext ctx; + private final Set emittedSchemas = new HashSet<>(); CodegenVisitor(PluginContext context) { // Load all integrations. @@ -286,10 +287,6 @@ void execute() { ctx.writerDelegator().useFileWriter("type_registry.go", settings.getModuleName(), new TypeRegistry(ctx)); ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", writer -> { - for (Shape shape : shapes) { - new SchemaGenerator(ctx, shape).accept(writer); - } - var operations = TopDownIndex.of(model).getContainedOperations(service); for (OperationShape op : operations) { new SchemaGenerator(ctx, op).accept(writer); @@ -314,13 +311,17 @@ void execute() { // ListMember() are non-nil when the member schema is created. var shapeIds = new HashSet(); for (Shape s : shapes) { - shapeIds.add(s.getId()); + if (!s.getId().getNamespace().equals(CodegenUtils.getSyntheticTypeNamespace())) { + shapeIds.add(s.getId()); + } } var sorted = new ArrayList(); var visited = new HashSet(); for (Shape s : shapes) { - topoVisit(s, model, shapeIds, visited, sorted); + if (shapeIds.contains(s.getId())) { + topoVisit(s, model, shapeIds, visited, sorted); + } } for (Shape shape : sorted) { @@ -427,17 +428,41 @@ void execute() { @Override protected Void getDefault(Shape shape) { + if (settings.useExperimentalSerde() + && !shape.isMemberShape() + && !shape.isOperationShape() + && !shape.isServiceShape() + && !shape.getId().getNamespace().equals(CodegenUtils.getSyntheticTypeNamespace()) + && emittedSchemas.add(shape.getId())) { + ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", + writer -> new SchemaGenerator(ctx, shape).accept(writer)); + } return null; } @Override public Void structureShape(StructureShape shape) { if (shape.getId().getNamespace().equals(CodegenUtils.getSyntheticTypeNamespace())) { + // For synthetic clones with an archetype, generate the schema under + // the archetype's name. Stub synthetics (no archetype) are skipped. + if (settings.useExperimentalSerde() && !CodegenUtils.isStubSynthetic(shape)) { + var archetypeId = shape.getTrait(Synthetic.class).get().getArchetype().get(); + if (emittedSchemas.add(archetypeId)) { + var archetype = model.expectShape(archetypeId, StructureShape.class); + ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", + writer -> new SchemaGenerator(ctx, archetype).accept(writer)); + } + } return null; } writers.useShapeWriter(shape, writer -> new StructureGenerator(ctx, writer, shape, protocolGenerator).run()); + if (settings.useExperimentalSerde() && emittedSchemas.add(shape.getId())) { + ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", + writer -> new SchemaGenerator(ctx, shape).accept(writer)); + } + return null; } @@ -446,6 +471,10 @@ public Void stringShape(StringShape shape) { if (shape.hasTrait(EnumTrait.class)) { writers.useShapeWriter(shape, writer -> new EnumGenerator(symbolProvider, writer, shape).run()); } + if (settings.useExperimentalSerde() && emittedSchemas.add(shape.getId())) { + ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", + writer -> new SchemaGenerator(ctx, shape).accept(writer)); + } return null; } @@ -455,8 +484,9 @@ public Void unionShape(UnionShape shape) { writers.useShapeWriter(shape, generator::generateUnion); writers.useShapeExportedTestWriter(shape, generator::generateUnionExamples); - if (settings.useExperimentalSerde()) { - // TODO schema probably + if (settings.useExperimentalSerde() && emittedSchemas.add(shape.getId())) { + ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", + writer -> new SchemaGenerator(ctx, shape).accept(writer)); } return null; } @@ -521,6 +551,10 @@ public Void serviceShape(ServiceShape shape) { @Override public Void intEnumShape(IntEnumShape shape) { writers.useShapeWriter(shape, writer -> new IntEnumGenerator(symbolProvider, writer, shape).run()); + if (settings.useExperimentalSerde() && emittedSchemas.add(shape.getId())) { + ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", + writer -> new SchemaGenerator(ctx, shape).accept(writer)); + } return null; } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java index 51e196b7e..94d44c0df 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java @@ -292,8 +292,10 @@ private void generateOperationProtocolMiddlewareAdders() { } else { writer.addUseImports(SmithyGoDependency.SMITHY); var opSchemaName = SchemaGenerator.getSchemaRef(operation, service); - var inputSchemaName = SchemaGenerator.getSchemaRef(input, service); - var outputSchemaName = SchemaGenerator.getSchemaRef(output, service); + var inputSchemaName = CodegenUtils.isStubSynthetic(input) + ? "nil" : SchemaGenerator.getSchemaRef(input, service); + var outputSchemaName = CodegenUtils.isStubSynthetic(output) + ? "nil" : SchemaGenerator.getSchemaRef(output, service); var opSchema = String.format("smithy.NewOperationSchema(%s, %s, %s)", opSchemaName, inputSchemaName, outputSchemaName); writer.write(""" diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java index f67dd80dc..016d06233 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/SchemaGenerator.java @@ -25,21 +25,11 @@ public SchemaGenerator(GoCodegenContext ctx, Shape shape) { this.shape = shape; } - // TODO(serde2): synthetic shapes (smithy.go.synthetic namespace) can - // collide with modeled shapes that happen to have the same name (e.g. - // ConverseOutput is both a modeled union and a synthetic operation output - // in bedrockruntime). For now just throw a prefix on there so it doesn't - // conflict but I don't like that. - private static final String SYNTHETIC_NAMESPACE = "smithy.go.synthetic"; - public static String getSchemaName(Shape shape, ServiceShape service) { if (Prelude.isPublicPreludeShape(shape)) { return "smithyprelude." + shape.getId().getName(); } - var name = shape.getId().getName(service); - if (shape.getId().getNamespace().equals(SYNTHETIC_NAMESPACE)) { - name = "SmithyGoSynthetic_" + name; - } + var name = modelShapeID(shape).getName(service); return ShapeUtil.isExported(shape) ? name : "_" + name; @@ -63,8 +53,12 @@ private static ShapeId modelShapeID(Shape shape) { // Returns the schema reference for use outside the schemas package (e.g. in // serde generators). Prelude shapes are already package-qualified, local - // shapes get the "schemas." prefix. + // shapes get the "schemas." prefix. Stub synthetics (no archetype) return + // "nil" since no schema is generated for them. public static String getSchemaRef(Shape shape, ServiceShape service) { + if (CodegenUtils.isStubSynthetic(shape)) { + return "nil"; + } if (Prelude.isPublicPreludeShape(shape)) { return getSchemaName(shape, service); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java index 37d948377..ef7f28683 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java @@ -2,9 +2,14 @@ import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; +import java.util.HashSet; import java.util.Map; +import java.util.stream.Collectors; +import software.amazon.smithy.model.knowledge.EventStreamIndex; +import software.amazon.smithy.model.knowledge.TopDownIndex; import software.amazon.smithy.model.loader.Prelude; import software.amazon.smithy.model.shapes.StructureShape; +import software.amazon.smithy.model.traits.ErrorTrait; public class TypeRegistry implements Writable { private final GoCodegenContext ctx; @@ -15,10 +20,28 @@ public TypeRegistry(GoCodegenContext ctx) { @Override public void accept(GoWriter writer) { - var shapes = ctx.serdeShapes(StructureShape.class).stream().toList(); + var model = ctx.model(); + var service = ctx.service(); + + // Errors: needed for DeserializableError lookup + var shapes = new HashSet(); + shapes.addAll(model.getStructureShapesWithTrait(ErrorTrait.class)); + + // Event stream events: needed for LookupEntry by target ID + var streamIndex = EventStreamIndex.of(model); + for (var op : TopDownIndex.of(model).getContainedOperations(service)) { + streamIndex.getInputInfo(op).ifPresent(info -> + info.getEventStreamTarget().members().forEach(m -> + shapes.add(model.expectShape(m.getTarget(), StructureShape.class)))); + streamIndex.getOutputInfo(op).ifPresent(info -> + info.getEventStreamTarget().members().forEach(m -> + shapes.add(model.expectShape(m.getTarget(), StructureShape.class)))); + } + + var sorted = shapes.stream().sorted().collect(Collectors.toList()); writer.addUseImports(SmithyGoDependency.SMITHY); - if (shapes.stream().anyMatch(Prelude::isPublicPreludeShape)) { + if (sorted.stream().anyMatch(Prelude::isPublicPreludeShape)) { writer.addUseImports(SmithyGoDependency.SMITHY_PRELUDE); } writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); @@ -29,7 +52,7 @@ public void accept(GoWriter writer) { $entries:W }, } - """, Map.of("entries", Writable.map(shapes, this::renderEntry))); + """, Map.of("entries", Writable.map(sorted, this::renderEntry))); } private Writable renderEntry(StructureShape shape) { diff --git a/schema.go b/schema.go index 410e1542f..dddfeae6d 100644 --- a/schema.go +++ b/schema.go @@ -298,6 +298,9 @@ func schemaTrait[T Trait](s *Schema, directOnly bool) (T, bool) { } func isEventStream(s *Schema) bool { + if s == nil { + return false + } for _, m := range s.members { if m.typ != ShapeTypeUnion { continue From a10992e5dba329a784a347433c7fb32bbca11189 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Fri, 15 May 2026 14:07:13 -0400 Subject: [PATCH 17/38] gate schemaserde doc thing behind useExperimentalSerde --- .../amazon/smithy/go/codegen/ProtocolDocumentGenerator.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java index 8074847c2..2b091ab36 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java @@ -420,8 +420,9 @@ private void writeInternalDocumentImplementation( }); writer.write(""); - // for schema-serde (if the service doesn't do that yet then this is just unused for now) - writer.write("func (m $P) Value() any { return m.value }", typeSymbol); + if (settings.useExperimentalSerde()) { + writer.write("func (m $P) Value() any { return m.value }", typeSymbol); + } writer.openBlock("func (m $P) $L(v interface{}) error {", "}", typeSymbol, UNMARSHAL_SMITHY_DOCUMENT_METHOD, unmarshalMethodDefinition); From c84473851e7bf9ec915100b1d8c18c915adeb356 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Fri, 15 May 2026 14:51:44 -0400 Subject: [PATCH 18/38] a --- .../amazon/smithy/go/codegen/ProtocolDocumentGenerator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java index 2b091ab36..142d71c85 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java @@ -426,6 +426,7 @@ private void writeInternalDocumentImplementation( writer.openBlock("func (m $P) $L(v interface{}) error {", "}", typeSymbol, UNMARSHAL_SMITHY_DOCUMENT_METHOD, unmarshalMethodDefinition); + writer.write(""); writer.openBlock("func (m $P) $L() ([]byte, error) {", "}", typeSymbol, MARSHAL_SMITHY_DOCUMENT_METHOD, marshalMethodDefinition); writer.write(""); From a9c2c4da3f4bb5a415b756187b484331760be1af Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Fri, 15 May 2026 18:37:22 -0400 Subject: [PATCH 19/38] generate endpoint and auth code separate from protocol generator (#662) --- .../smithy/go/codegen/CodegenVisitor.java | 40 ++++++++---------- .../smithy/go/codegen/auth/AuthGenerator.java | 38 +++++++---------- .../codegen/auth/AuthParametersGenerator.java | 12 +++--- .../auth/AuthParametersResolverGenerator.java | 12 +++--- .../auth/AuthSchemeResolverGenerator.java | 39 +++++++++++------- .../auth/GetIdentityMiddlewareGenerator.java | 6 +-- .../ResolveAuthSchemeMiddlewareGenerator.java | 6 +-- .../auth/SignRequestMiddlewareGenerator.java | 6 +-- .../EndpointMiddlewareGenerator.java | 16 ++++---- .../EndpointParameterBindingsGenerator.java | 41 ++++++++++++------- .../EndpointResolutionGenerator.java | 27 ++++-------- .../go/codegen/integration/GoIntegration.java | 9 ++++ .../integration/ProtocolGenerator.java | 8 ---- 13 files changed, 122 insertions(+), 138 deletions(-) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java index 904c5deb4..be68cde2c 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java @@ -33,6 +33,10 @@ import software.amazon.smithy.codegen.core.SymbolDependency; import software.amazon.smithy.codegen.core.SymbolProvider; import software.amazon.smithy.go.codegen.GoSettings.ArtifactType; +import software.amazon.smithy.go.codegen.auth.AuthGenerator; +import software.amazon.smithy.go.codegen.endpoints.EndpointResolutionGenerator; +import software.amazon.smithy.go.codegen.endpoints.FnGenerator; +import software.amazon.smithy.go.codegen.endpoints.FnProvider; import software.amazon.smithy.go.codegen.integration.GoIntegration; import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; import software.amazon.smithy.go.codegen.integration.RuntimeClientPlugin; @@ -381,33 +385,21 @@ void execute() { } } - // This is all stuff that we'll still need to do but it DEPENDS on ProtocolGenerator right now - // - // TODO: With serde/schema decoupling, protocol generators are going away. Endpoint/auth resolution is going to - // need to be decoupled from that and I don't really know how yet. Probably just separate integrations / - // integration hooks. - if (protocolGenerator != null) { - ProtocolGenerator.GenerationContext.Builder contextBuilder = ProtocolGenerator.GenerationContext.builder() - .protocolName(protocolGenerator.getProtocolName()) - .integrations(integrations) - .model(model) - .service(service) - .settings(settings) - .symbolProvider(symbolProvider) - .delegator(writers); + { + FnProvider fnProvider = integrations.stream() + .map(GoIntegration::getEndpointFnProvider) + .filter(java.util.Objects::nonNull) + .findFirst() + .orElse(new FnGenerator.DefaultFnProvider()); + var endpointGenerator = new EndpointResolutionGenerator(fnProvider); writers.useFileWriter("endpoints.go", settings.getModuleName(), writer -> { - ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); - protocolGenerator.generateEndpointResolution(context); - }); - - writers.useFileWriter("auth.go", settings.getModuleName(), writer -> { - ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); - protocolGenerator.generateAuth(context); + endpointGenerator.generate(ctx, writer); }); - writers.useFileWriter("endpoints_test.go", settings.getModuleName(), writer -> { - ProtocolGenerator.GenerationContext context = contextBuilder.writer(writer).build(); - protocolGenerator.generateEndpointResolutionTests(context); + endpointGenerator.generateTests(ctx, writer); + }); + writers.useFileWriter("auth.go", settings.getModuleName(), writer -> { + new AuthGenerator(ctx, writer).generate(); }); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthGenerator.java index bc5d4b01e..c396da00a 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthGenerator.java @@ -15,35 +15,25 @@ package software.amazon.smithy.go.codegen.auth; -import software.amazon.smithy.codegen.core.CodegenException; -import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; +import software.amazon.smithy.go.codegen.GoCodegenContext; +import software.amazon.smithy.go.codegen.GoWriter; -/** - * Entry point into smithy client auth generation. - */ public class AuthGenerator { - private final ProtocolGenerator.GenerationContext context; + private final GoCodegenContext ctx; + private final GoWriter writer; - public AuthGenerator(ProtocolGenerator.GenerationContext context) { - this.context = context; + public AuthGenerator(GoCodegenContext ctx, GoWriter writer) { + this.ctx = ctx; + this.writer = writer; } public void generate() { - if (context.getWriter().isEmpty()) { - throw new CodegenException("writer is required"); - } - - context.getWriter().get() - .write("$W\n", new AuthParametersGenerator(context).generate()) - .write("$W\n", new AuthParametersResolverGenerator(context).generate()) - .write("$W\n", getResolverGenerator().generate()) - .write("$W\n", new ResolveAuthSchemeMiddlewareGenerator(context).generate()) - .write("$W\n", new GetIdentityMiddlewareGenerator(context).generate()) - .write("$W\n", new SignRequestMiddlewareGenerator(context).generate()); - } - - // TODO(i&a): allow consuming generators to overwrite - private AuthSchemeResolverGenerator getResolverGenerator() { - return new AuthSchemeResolverGenerator(context); + writer + .write("$W\n", new AuthParametersGenerator(ctx).generate()) + .write("$W\n", new AuthParametersResolverGenerator(ctx).generate()) + .write("$W\n", new AuthSchemeResolverGenerator(ctx).generate()) + .write("$W\n", new ResolveAuthSchemeMiddlewareGenerator().generate()) + .write("$W\n", new GetIdentityMiddlewareGenerator().generate()) + .write("$W\n", new SignRequestMiddlewareGenerator().generate()); } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthParametersGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthParametersGenerator.java index 0db56e007..08d97c6b8 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthParametersGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthParametersGenerator.java @@ -20,9 +20,9 @@ import java.util.ArrayList; import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.go.codegen.GoCodegenContext; import software.amazon.smithy.go.codegen.SymbolUtils; import software.amazon.smithy.go.codegen.Writable; -import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; import software.amazon.smithy.utils.ListUtils; import software.amazon.smithy.utils.MapUtils; @@ -37,14 +37,14 @@ public class AuthParametersGenerator { public static final Symbol STRUCT_SYMBOL = SymbolUtils.createPointableSymbolBuilder(STRUCT_NAME).build(); - private final ProtocolGenerator.GenerationContext context; + private final GoCodegenContext ctx; private final ArrayList fields = new ArrayList<>( ListUtils.of(AuthParameter.OPERATION) ); - public AuthParametersGenerator(ProtocolGenerator.GenerationContext context) { - this.context = context; + public AuthParametersGenerator(GoCodegenContext ctx) { + this.ctx = ctx; } public Writable generate() { @@ -88,9 +88,9 @@ private Writable generateFields() { } private void loadFields() { - for (var integration: context.getIntegrations()) { + for (var integration: ctx.integrations()) { var plugins = integration.getClientPlugins().stream().filter(it -> - it.matchesService(context.getModel(), context.getService())).toList(); + it.matchesService(ctx.model(), ctx.service())).toList(); for (var plugin: plugins) { fields.addAll(plugin.getAuthParameters()); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthParametersResolverGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthParametersResolverGenerator.java index b2959ff98..ffa365b00 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthParametersResolverGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthParametersResolverGenerator.java @@ -18,9 +18,9 @@ import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; import java.util.ArrayList; +import software.amazon.smithy.go.codegen.GoCodegenContext; import software.amazon.smithy.go.codegen.GoStdlibTypes; import software.amazon.smithy.go.codegen.Writable; -import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; import software.amazon.smithy.utils.MapUtils; /** @@ -31,12 +31,12 @@ public class AuthParametersResolverGenerator { public static final String FUNC_NAME = "bindAuthResolverParams"; - private final ProtocolGenerator.GenerationContext context; + private final GoCodegenContext ctx; private final ArrayList resolvers = new ArrayList<>(); - public AuthParametersResolverGenerator(ProtocolGenerator.GenerationContext context) { - this.context = context; + public AuthParametersResolverGenerator(GoCodegenContext ctx) { + this.ctx = ctx; } public Writable generate() { @@ -74,9 +74,9 @@ private Writable generateResolvers() { } private void loadResolvers() { - for (var integration: context.getIntegrations()) { + for (var integration: ctx.integrations()) { var plugins = integration.getClientPlugins().stream().filter(it -> - it.matchesService(context.getModel(), context.getService())).toList(); + it.matchesService(ctx.model(), ctx.service())).toList(); for (var plugin: plugins) { resolvers.addAll(plugin.getAuthParameterResolvers()); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthSchemeResolverGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthSchemeResolverGenerator.java index c14259a0e..2dea29dd9 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthSchemeResolverGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/AuthSchemeResolverGenerator.java @@ -23,6 +23,7 @@ import java.util.stream.Collectors; import software.amazon.smithy.aws.traits.auth.UnsignedPayloadTrait; import software.amazon.smithy.go.codegen.ChainWritable; +import software.amazon.smithy.go.codegen.GoCodegenContext; import software.amazon.smithy.go.codegen.GoStdlibTypes; import software.amazon.smithy.go.codegen.Writable; import software.amazon.smithy.go.codegen.integration.AuthSchemeDefinition; @@ -41,15 +42,25 @@ public class AuthSchemeResolverGenerator { public static final String INTERFACE_NAME = "AuthSchemeResolver"; public static final String DEFAULT_NAME = "defaultAuthSchemeResolver"; - private final ProtocolGenerator.GenerationContext context; + private final GoCodegenContext ctx; + private final ProtocolGenerator.GenerationContext legacyContext; private final ServiceIndex serviceIndex; private final Map schemeDefinitions; - public AuthSchemeResolverGenerator(ProtocolGenerator.GenerationContext context) { - this.context = context; - this.serviceIndex = ServiceIndex.of(context.getModel()); - this.schemeDefinitions = context.getIntegrations().stream() - .flatMap(it -> it.getClientPlugins(context.getModel(), context.getService()).stream()) + public AuthSchemeResolverGenerator(GoCodegenContext ctx) { + this.ctx = ctx; + this.legacyContext = ProtocolGenerator.GenerationContext.builder() + .protocolName("") + .integrations(ctx.integrations()) + .model(ctx.model()) + .service(ctx.service()) + .settings(ctx.settings()) + .symbolProvider(ctx.symbolProvider()) + .delegator((software.amazon.smithy.go.codegen.GoDelegator) ctx.writerDelegator()) + .build(); + this.serviceIndex = ServiceIndex.of(ctx.model()); + this.schemeDefinitions = ctx.integrations().stream() + .flatMap(it -> it.getClientPlugins(ctx.model(), ctx.service()).stream()) .flatMap(it -> it.getAuthSchemeDefinitions().entrySet().stream()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } @@ -60,10 +71,10 @@ public AuthSchemeResolverGenerator(ProtocolGenerator.GenerationContext context) // 3. it has an unsigned payload private boolean hasAuthOverrides(OperationShape operation) { var serviceSchemes = serviceIndex - .getEffectiveAuthSchemes(context.getService(), ServiceIndex.AuthSchemeMode.NO_AUTH_AWARE) + .getEffectiveAuthSchemes(ctx.service(), ServiceIndex.AuthSchemeMode.NO_AUTH_AWARE) .keySet(); var operationSchemes = serviceIndex - .getEffectiveAuthSchemes(context.getService(), operation, ServiceIndex.AuthSchemeMode.NO_AUTH_AWARE) + .getEffectiveAuthSchemes(ctx.service(), operation, ServiceIndex.AuthSchemeMode.NO_AUTH_AWARE) .keySet(); return !serviceSchemes.equals(operationSchemes) || operation.hasTrait(UnsignedPayloadTrait.class); } @@ -136,8 +147,8 @@ private Writable generateDefaultResolve() { private Writable generateOperationAuthOptions() { var options = new ChainWritable(); - TopDownIndex.of(context.getModel()) - .getContainedOperations(context.getService()).stream() + TopDownIndex.of(ctx.model()) + .getContainedOperations(ctx.service()).stream() .filter(this::hasAuthOverrides) .forEach(it -> { options.add(generateOperationAuthOptionsEntry(it)); @@ -156,12 +167,12 @@ private Writable generateOperationAuthOptions() { private Writable generateOperationAuthOptionsEntry(OperationShape operation) { var options = new ChainWritable(); serviceIndex - .getEffectiveAuthSchemes(context.getService(), operation, ServiceIndex.AuthSchemeMode.NO_AUTH_AWARE) + .getEffectiveAuthSchemes(ctx.service(), operation, ServiceIndex.AuthSchemeMode.NO_AUTH_AWARE) .entrySet().stream() .filter(it -> schemeDefinitions.containsKey(it.getKey())) .forEach(it -> { var definition = schemeDefinitions.get(it.getKey()); - options.add(definition.generateOperationOption(context, operation)); + options.add(definition.generateOperationOption(legacyContext, operation)); }); return options.isEmpty() @@ -181,12 +192,12 @@ private Writable generateOperationAuthOptionsEntry(OperationShape operation) { private Writable generateServiceAuthOptions() { var options = new ChainWritable(); serviceIndex - .getEffectiveAuthSchemes(context.getService(), ServiceIndex.AuthSchemeMode.NO_AUTH_AWARE) + .getEffectiveAuthSchemes(ctx.service(), ServiceIndex.AuthSchemeMode.NO_AUTH_AWARE) .entrySet().stream() .filter(it -> schemeDefinitions.containsKey(it.getKey())) .forEach(it -> { var definition = schemeDefinitions.get(it.getKey()); - options.add(definition.generateServiceOption(context, context.getService())); + options.add(definition.generateServiceOption(legacyContext, ctx.service())); }); return goTemplate(""" diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/GetIdentityMiddlewareGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/GetIdentityMiddlewareGenerator.java index 10b51a23d..1d7bfb6e4 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/GetIdentityMiddlewareGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/GetIdentityMiddlewareGenerator.java @@ -23,17 +23,13 @@ import software.amazon.smithy.go.codegen.MiddlewareIdentifier; import software.amazon.smithy.go.codegen.SmithyGoDependency; import software.amazon.smithy.go.codegen.Writable; -import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; import software.amazon.smithy.utils.MapUtils; public class GetIdentityMiddlewareGenerator { public static final String MIDDLEWARE_NAME = "getIdentityMiddleware"; public static final String MIDDLEWARE_ID = "GetIdentity"; - private final ProtocolGenerator.GenerationContext context; - - public GetIdentityMiddlewareGenerator(ProtocolGenerator.GenerationContext context) { - this.context = context; + public GetIdentityMiddlewareGenerator() { } public static Writable generateAddToProtocolFinalizers() { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/ResolveAuthSchemeMiddlewareGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/ResolveAuthSchemeMiddlewareGenerator.java index 661df269f..0b76a28c8 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/ResolveAuthSchemeMiddlewareGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/ResolveAuthSchemeMiddlewareGenerator.java @@ -24,17 +24,13 @@ import software.amazon.smithy.go.codegen.MiddlewareIdentifier; import software.amazon.smithy.go.codegen.SmithyGoDependency; import software.amazon.smithy.go.codegen.Writable; -import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; import software.amazon.smithy.utils.MapUtils; public class ResolveAuthSchemeMiddlewareGenerator { public static final String MIDDLEWARE_NAME = "resolveAuthSchemeMiddleware"; public static final String MIDDLEWARE_ID = "ResolveAuthScheme"; - private final ProtocolGenerator.GenerationContext context; - - public ResolveAuthSchemeMiddlewareGenerator(ProtocolGenerator.GenerationContext context) { - this.context = context; + public ResolveAuthSchemeMiddlewareGenerator() { } public static Writable generateAddToProtocolFinalizers() { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/SignRequestMiddlewareGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/SignRequestMiddlewareGenerator.java index 8bb1dc5a3..59973eb31 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/SignRequestMiddlewareGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/auth/SignRequestMiddlewareGenerator.java @@ -23,17 +23,13 @@ import software.amazon.smithy.go.codegen.SmithyGoDependency; import software.amazon.smithy.go.codegen.Writable; import software.amazon.smithy.go.codegen.endpoints.EndpointMiddlewareGenerator; -import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; import software.amazon.smithy.utils.MapUtils; public class SignRequestMiddlewareGenerator { public static final String MIDDLEWARE_NAME = "signRequestMiddleware"; public static final String MIDDLEWARE_ID = "Signing"; - private final ProtocolGenerator.GenerationContext context; - - public SignRequestMiddlewareGenerator(ProtocolGenerator.GenerationContext context) { - this.context = context; + public SignRequestMiddlewareGenerator() { } public static Writable generateAddToProtocolFinalizers() { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointMiddlewareGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointMiddlewareGenerator.java index 2712015ce..72a49df38 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointMiddlewareGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointMiddlewareGenerator.java @@ -19,13 +19,13 @@ import static software.amazon.smithy.go.codegen.GoWriter.goTemplate; import static software.amazon.smithy.go.codegen.SmithyGoDependency.SMITHY_TRACING; +import software.amazon.smithy.go.codegen.GoCodegenContext; import software.amazon.smithy.go.codegen.GoStdlibTypes; import software.amazon.smithy.go.codegen.GoWriter; import software.amazon.smithy.go.codegen.MiddlewareIdentifier; import software.amazon.smithy.go.codegen.Writable; import software.amazon.smithy.go.codegen.auth.GetIdentityMiddlewareGenerator; import software.amazon.smithy.go.codegen.integration.GoIntegration; -import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; import software.amazon.smithy.go.codegen.SmithyGoDependency; import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait; import software.amazon.smithy.utils.MapUtils; @@ -38,10 +38,10 @@ public final class EndpointMiddlewareGenerator { public static final String MIDDLEWARE_NAME = "resolveEndpointV2Middleware"; public static final String MIDDLEWARE_ID = "ResolveEndpointV2"; - private final ProtocolGenerator.GenerationContext context; + private final GoCodegenContext ctx; - public EndpointMiddlewareGenerator(ProtocolGenerator.GenerationContext context) { - this.context = context; + public EndpointMiddlewareGenerator(GoCodegenContext ctx) { + this.ctx = ctx; } public static Writable generateAddToProtocolFinalizers() { @@ -101,8 +101,8 @@ private Writable generateBody() { private Writable generatePreResolutionHooks() { return (GoWriter writer) -> { - for (GoIntegration integration : context.getIntegrations()) { - integration.renderPreEndpointResolutionHook(context.getSettings(), writer, context.getModel()); + for (GoIntegration integration : ctx.integrations()) { + integration.renderPreEndpointResolutionHook(ctx.settings(), writer, ctx.model()); } }; } @@ -186,8 +186,8 @@ private Writable generateMergeAuthProperties() { private Writable generatePostResolutionHooks() { return (GoWriter writer) -> { - for (GoIntegration integration : context.getIntegrations()) { - integration.renderPostEndpointResolutionHook(context.getSettings(), writer, context.getModel()); + for (GoIntegration integration : ctx.integrations()) { + integration.renderPostEndpointResolutionHook(ctx.settings(), writer, ctx.model()); } }; } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointParameterBindingsGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointParameterBindingsGenerator.java index 054e10e3c..72de2b65c 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointParameterBindingsGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointParameterBindingsGenerator.java @@ -22,9 +22,10 @@ import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; +import software.amazon.smithy.go.codegen.GoCodegenContext; import software.amazon.smithy.go.codegen.GoStdlibTypes; import software.amazon.smithy.go.codegen.Writable; -import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; +import software.amazon.smithy.rulesengine.language.EndpointRuleSet; import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameter; import software.amazon.smithy.rulesengine.traits.ClientContextParamsTrait; import software.amazon.smithy.rulesengine.traits.EndpointBddTrait; @@ -36,14 +37,14 @@ * conditionally through EndpointParameterOperationBindingsGenerator. */ public class EndpointParameterBindingsGenerator { - private final ProtocolGenerator.GenerationContext context; + private final GoCodegenContext ctx; private final Map builtinBindings; - public EndpointParameterBindingsGenerator(ProtocolGenerator.GenerationContext context) { - this.context = context; - this.builtinBindings = context.getIntegrations().stream() - .flatMap(it -> it.getClientPlugins(context.getModel(), context.getService()).stream()) + public EndpointParameterBindingsGenerator(GoCodegenContext ctx) { + this.ctx = ctx; + this.builtinBindings = ctx.integrations().stream() + .flatMap(it -> it.getClientPlugins(ctx.model(), ctx.service()).stream()) .flatMap(it -> it.getEndpointBuiltinBindings().entrySet().stream()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } @@ -70,8 +71,8 @@ func bindEndpointParams(ctx $context:T, input interface{}, options Options) (*En """, MapUtils.of( "context", GoStdlibTypes.Context.Context, - "builtinBindings", context.getService().hasTrait(EndpointRuleSetTrait.class) - || context.getService().hasTrait(EndpointBddTrait.class) + "builtinBindings", ctx.service().hasTrait(EndpointRuleSetTrait.class) + || ctx.service().hasTrait(EndpointBddTrait.class) ? generateBuiltinBindings() : emptyGoTemplate(), "clientContextBindings", generateClientContextBindings() @@ -80,15 +81,15 @@ func bindEndpointParams(ctx $context:T, input interface{}, options Options) (*En private Writable generateBuiltinBindings() { var bindings = new HashMap(); - for (var integration: context.getIntegrations()) { - var plugins = integration.getClientPlugins(context.getModel(), context.getService()); + for (var integration: ctx.integrations()) { + var plugins = integration.getClientPlugins(ctx.model(), ctx.service()); for (var plugin: plugins) { bindings.putAll(plugin.getEndpointBuiltinBindings()); } } var params = new ArrayList(); - context.getEndpointRules().getParameters().forEach(params::add); + getEndpointRules().getParameters().forEach(params::add); var boundBuiltins = params.stream() .filter(it -> it.isBuiltIn() && bindings.containsKey(it.getBuiltIn().get())) .toList(); @@ -115,13 +116,13 @@ private Writable generateBuiltinBindings() { } private Writable generateClientContextBindings() { - if (!context.getService().hasTrait(ClientContextParamsTrait.class)) { + if (!ctx.service().hasTrait(ClientContextParamsTrait.class)) { return goTemplate(""); } var allParams = new ArrayList(); - context.getEndpointRules().getParameters().forEach(allParams::add); - var contextParams = context.getService().expectTrait(ClientContextParamsTrait.class).getParameters(); + getEndpointRules().getParameters().forEach(allParams::add); + var contextParams = ctx.service().expectTrait(ClientContextParamsTrait.class).getParameters(); var params = allParams.stream() .filter(it -> contextParams.containsKey(it.getName().getName().getValue()) && !it.isBuiltIn()) .toList(); @@ -132,4 +133,16 @@ private Writable generateClientContextBindings() { }); }; } + + private EndpointRuleSet getEndpointRules() { + var service = ctx.service(); + if (service.hasTrait(EndpointRuleSetTrait.class)) { + return EndpointRuleSet.fromNode(service.expectTrait(EndpointRuleSetTrait.class).getRuleSet()); + } + var bddTrait = service.expectTrait(EndpointBddTrait.class); + return EndpointRuleSet.builder() + .version(bddTrait.getVersion().toString()) + .parameters(bddTrait.getParameters()) + .build(); + } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointResolutionGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointResolutionGenerator.java index d2a008b51..d76de9ac9 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointResolutionGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/endpoints/EndpointResolutionGenerator.java @@ -19,11 +19,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -import software.amazon.smithy.codegen.core.CodegenException; import software.amazon.smithy.codegen.core.Symbol; +import software.amazon.smithy.go.codegen.GoCodegenContext; +import software.amazon.smithy.go.codegen.GoWriter; import software.amazon.smithy.go.codegen.SmithyGoDependency; import software.amazon.smithy.go.codegen.SymbolUtils; -import software.amazon.smithy.go.codegen.integration.ProtocolGenerator; import software.amazon.smithy.rulesengine.language.EndpointRuleSet; import software.amazon.smithy.rulesengine.traits.EndpointBddTrait; import software.amazon.smithy.rulesengine.traits.EndpointRuleSetTrait; @@ -62,14 +62,8 @@ public EndpointResolutionGenerator(FnProvider fnProvider) { this.newResolverFn = SymbolUtils.createValueSymbolBuilder(NEW_RESOLVER_FUNC_NAME).build(); } - public void generate(ProtocolGenerator.GenerationContext context) { - if (context.getWriter().isEmpty()) { - throw new CodegenException("writer is required"); - } - - var writer = context.getWriter().get(); - - var serviceShape = context.getService(); + public void generate(GoCodegenContext ctx, GoWriter writer) { + var serviceShape = ctx.service(); var parametersGenerator = EndpointParametersGenerator.builder() .parametersType(parametersType) @@ -85,8 +79,8 @@ public void generate(ProtocolGenerator.GenerationContext context) { .fnProvider(this.fnProvider) .build(); - var bindingGenerator = new EndpointParameterBindingsGenerator(context); - var middlewareGenerator = new EndpointMiddlewareGenerator(context); + var bindingGenerator = new EndpointParameterBindingsGenerator(ctx); + var middlewareGenerator = new EndpointMiddlewareGenerator(ctx); // Ensure rulesfn import for StringSlice used in scope-emitted code. writer.write("var _ = $T(nil)", SymbolUtils.createValueSymbolBuilder( @@ -122,13 +116,8 @@ public void generate(ProtocolGenerator.GenerationContext context) { .write("$W", middlewareGenerator.generate()); } - public void generateTests(ProtocolGenerator.GenerationContext context) { - if (context.getWriter().isEmpty()) { - throw new CodegenException("writer is required"); - } - - var writer = context.getWriter().get(); - var serviceShape = context.getService(); + public void generateTests(GoCodegenContext ctx, GoWriter writer) { + var serviceShape = ctx.service(); Optional ruleset = serviceShape.getTrait(EndpointRuleSetTrait.class) .map((trait) -> EndpointRuleSet.fromNode(trait.getRuleSet())); if (ruleset.isEmpty()) { diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/GoIntegration.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/GoIntegration.java index 9d39e9fb1..ad0c6d4a1 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/GoIntegration.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/GoIntegration.java @@ -23,6 +23,7 @@ import software.amazon.smithy.go.codegen.GoCodegenContext; import software.amazon.smithy.go.codegen.GoDelegator; import software.amazon.smithy.go.codegen.GoSettings; +import software.amazon.smithy.go.codegen.endpoints.FnProvider; import software.amazon.smithy.go.codegen.GoSettings.ArtifactType; import software.amazon.smithy.go.codegen.GoWriter; import software.amazon.smithy.go.codegen.TriConsumer; @@ -173,6 +174,14 @@ default List getProtocolGenerators() { return Collections.emptyList(); } + /** + * Gets a custom endpoint rule function provider. Returning null (the default) indicates no custom functions. + * The returned provider is consulted before the built-in default provider. + */ + default FnProvider getEndpointFnProvider() { + return null; + } + /** * Gets a list of server protocol generators to register. Protocol generators should generally be written to accept * the codegen context at construction time, such that all the model information necessary for codegen is available. diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ProtocolGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ProtocolGenerator.java index cb6080783..6a81927a7 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ProtocolGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/ProtocolGenerator.java @@ -29,9 +29,6 @@ import software.amazon.smithy.go.codegen.GoSettings; import software.amazon.smithy.go.codegen.GoWriter; import software.amazon.smithy.go.codegen.Synthetic; -import software.amazon.smithy.go.codegen.auth.AuthGenerator; -import software.amazon.smithy.go.codegen.endpoints.EndpointResolutionGenerator; -import software.amazon.smithy.go.codegen.endpoints.FnGenerator; import software.amazon.smithy.model.Model; import software.amazon.smithy.model.node.ExpectationNotMetException; import software.amazon.smithy.model.shapes.OperationShape; @@ -462,8 +459,6 @@ default void generateEventStreamComponents(GenerationContext context) { * @param context the generation context. */ default void generateEndpointResolution(GenerationContext context) { - var generator = new EndpointResolutionGenerator(new FnGenerator.DefaultFnProvider()); - generator.generate(context); } /** @@ -472,8 +467,6 @@ default void generateEndpointResolution(GenerationContext context) { * @param context the generation context. */ default void generateEndpointResolutionTests(GenerationContext context) { - var generator = new EndpointResolutionGenerator(new FnGenerator.DefaultFnProvider()); - generator.generateTests(context); } /** @@ -482,7 +475,6 @@ default void generateEndpointResolutionTests(GenerationContext context) { * @param context The generation context. */ default void generateAuth(GenerationContext context) { - new AuthGenerator(context).generate(); } /** From 4a9408b8808143ac3d1d0b871723456a80844d38 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Fri, 15 May 2026 21:40:44 -0400 Subject: [PATCH 20/38] addressed TODO --- .../java/software/amazon/smithy/go/codegen/GoSettings.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java index 7059bc5f8..d68af2440 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java @@ -58,10 +58,6 @@ public final class GoSettings { private static final String USE_EXPERIMENTAL_SERDE = "useExperimentalSerde"; private static final String GO_DIRECTIVE = "goDirective"; - // TODO(serde2): add useLegacyUnknownMessageError setting for SDK backwards compat: - // 1. generates per-service UnknownEventMessageError type (with Message being the old SDK *eventstream.Message) - // 2. generates extra code in the reader adapter to map smithyeventstream.UnknownMessageError to .UnknownMessageError - private ShapeId service; private String moduleName; private String moduleDescription = ""; From 4f1aa62831c80ce358e22e5e1d7335795359a528 Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Mon, 18 May 2026 14:34:35 -0400 Subject: [PATCH 21/38] serde2: drop writeptr shenanigans and optimize allocs on loop deserialize (#663) * fix loopalloc * drop writeptr shenanigans --- .../internal/httpbinding/serializer.go | 140 ++---------------- .../internal/json/shape_serializer.go | 126 +--------------- .../internal/query/shape_serializer.go | 104 +------------ .../internal/xml/shape_serializer.go | 72 +-------- .../go/codegen/serde2/ListDeserializer.java | 16 +- .../go/codegen/serde2/MapDeserializer.java | 16 +- .../codegen/serde2/StructureSerializer.java | 48 ++++-- eventstream/serializer.go | 75 +--------- serde.go | 40 +++-- .../internal/cbor/shape_serializer.go | 121 +-------------- 10 files changed, 119 insertions(+), 639 deletions(-) diff --git a/aws-protocols/internal/httpbinding/serializer.go b/aws-protocols/internal/httpbinding/serializer.go index 6d94f77ab..ce0584379 100644 --- a/aws-protocols/internal/httpbinding/serializer.go +++ b/aws-protocols/internal/httpbinding/serializer.go @@ -55,9 +55,7 @@ type ShapeSerializer struct { } // ShapeSerializerOptions configures a ShapeSerializer. -type ShapeSerializerOptions struct { - WriteZeroValues bool -} +type ShapeSerializerOptions struct{} // NewShapeSerializer creates a ShapeSerializer for the given operation schema // and request. It handles the initial setup from use of an @@ -145,16 +143,6 @@ func (s *ShapeSerializer) setBody(body io.Reader, contentType string) error { return nil } -// withWriteZero allows temporarily setting to true the ShapeSerializer `options.WriteZeroValues` -// This is useful for writing pointer values that have an empty value, like `&""`, which would be skipped -// otherwise if `options.WriteZeroValues` would be set to false -func (s *ShapeSerializer) withWriteZero(fn func()) { - prev := s.options.WriteZeroValues - s.options.WriteZeroValues = true - fn() - s.options.WriteZeroValues = prev -} - type mapBindingMode int const ( @@ -283,9 +271,6 @@ func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { } bt, bn := s.resolveBinding(schema) - if v == "" && !s.options.WriteZeroValues && bt != bindLabel { - return - } switch bt { case bindHeaderList: escaped := v @@ -312,18 +297,7 @@ func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { s.httpPayloadContentType = "text/plain" return } - if s.options.WriteZeroValues { - s.input.WriteStringPtr(schema, &v) - } else { - s.input.WriteString(schema, v) - } - } -} - -// WriteStringPtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteStringPtr(schema *smithy.Schema, v *string) { - if v != nil { - s.withWriteZero(func() { s.WriteString(schema, *v) }) + s.input.WriteString(schema, v) } } @@ -343,18 +317,7 @@ func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { case bindQuery: s.encoder.SetQuery(bn).Boolean(v) default: - if s.options.WriteZeroValues { - s.input.WriteBoolPtr(schema, &v) - } else { - s.input.WriteBool(schema, v) - } - } -} - -// WriteBoolPtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteBoolPtr(schema *smithy.Schema, v *bool) { - if v != nil { - s.withWriteZero(func() { s.WriteBool(schema, *v) }) + s.input.WriteBool(schema, v) } } @@ -374,18 +337,7 @@ func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { case bindQuery: s.encoder.SetQuery(bn).Byte(v) default: - if s.options.WriteZeroValues { - s.input.WriteInt8Ptr(schema, &v) - } else { - s.input.WriteInt8(schema, v) - } - } -} - -// WriteInt8Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt8Ptr(schema *smithy.Schema, v *int8) { - if v != nil { - s.withWriteZero(func() { s.WriteInt8(schema, *v) }) + s.input.WriteInt8(schema, v) } } @@ -405,18 +357,7 @@ func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { case bindQuery: s.encoder.SetQuery(bn).Short(v) default: - if s.options.WriteZeroValues { - s.input.WriteInt16Ptr(schema, &v) - } else { - s.input.WriteInt16(schema, v) - } - } -} - -// WriteInt16Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt16Ptr(schema *smithy.Schema, v *int16) { - if v != nil { - s.withWriteZero(func() { s.WriteInt16(schema, *v) }) + s.input.WriteInt16(schema, v) } } @@ -436,18 +377,7 @@ func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { case bindQuery: s.encoder.SetQuery(bn).Integer(v) default: - if s.options.WriteZeroValues { - s.input.WriteInt32Ptr(schema, &v) - } else { - s.input.WriteInt32(schema, v) - } - } -} - -// WriteInt32Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt32Ptr(schema *smithy.Schema, v *int32) { - if v != nil { - s.withWriteZero(func() { s.WriteInt32(schema, *v) }) + s.input.WriteInt32(schema, v) } } @@ -467,18 +397,7 @@ func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { case bindQuery: s.encoder.SetQuery(bn).Long(v) default: - if s.options.WriteZeroValues { - s.input.WriteInt64Ptr(schema, &v) - } else { - s.input.WriteInt64(schema, v) - } - } -} - -// WriteInt64Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt64Ptr(schema *smithy.Schema, v *int64) { - if v != nil { - s.withWriteZero(func() { s.WriteInt64(schema, *v) }) + s.input.WriteInt64(schema, v) } } @@ -498,18 +417,7 @@ func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { case bindQuery: s.encoder.SetQuery(bn).Float(v) default: - if s.options.WriteZeroValues { - s.input.WriteFloat32Ptr(schema, &v) - } else { - s.input.WriteFloat32(schema, v) - } - } -} - -// WriteFloat32Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteFloat32Ptr(schema *smithy.Schema, v *float32) { - if v != nil { - s.withWriteZero(func() { s.WriteFloat32(schema, *v) }) + s.input.WriteFloat32(schema, v) } } @@ -529,18 +437,7 @@ func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { case bindQuery: s.encoder.SetQuery(bn).Double(v) default: - if s.options.WriteZeroValues { - s.input.WriteFloat64Ptr(schema, &v) - } else { - s.input.WriteFloat64(schema, v) - } - } -} - -// WriteFloat64Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteFloat64Ptr(schema *smithy.Schema, v *float64) { - if v != nil { - s.withWriteZero(func() { s.WriteFloat64(schema, *v) }) + s.input.WriteFloat64(schema, v) } } @@ -582,13 +479,6 @@ func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { } } -// WriteTimePtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { - if v != nil { - s.withWriteZero(func() { s.WriteTime(schema, *v) }) - } -} - // WriteList implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { if s.mapMode == mapModeQueryParams { @@ -714,14 +604,14 @@ func (s *ShapeSerializer) WriteNil(schema *smithy.Schema) { s.input.WriteNil(schema) } -// WriteBigInteger implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteBigInteger(schema *smithy.Schema, v big.Int) { - s.input.WriteBigInteger(schema, v) +// WriteBigInt implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBigInt(schema *smithy.Schema, v *big.Int) { + s.input.WriteBigInt(schema, v) } -// WriteBigDecimal implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteBigDecimal(schema *smithy.Schema, v big.Float) { - s.input.WriteBigDecimal(schema, v) +// WriteBigFloat implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBigFloat(schema *smithy.Schema, v *big.Float) { + s.input.WriteBigFloat(schema, v) } // WriteDocument implements [smithy.ShapeSerializer]. diff --git a/aws-protocols/internal/json/shape_serializer.go b/aws-protocols/internal/json/shape_serializer.go index ffb4ee434..78681e526 100644 --- a/aws-protocols/internal/json/shape_serializer.go +++ b/aws-protocols/internal/json/shape_serializer.go @@ -23,12 +23,6 @@ type ShapeSerializer struct { // Options configures JSON shape serialization and deserialization. type Options struct { - // Controls whether scalar zero values (numbers, strings, bools) are - // written. If false (the default), zero values are not encoded. - // - // Pointer write methods are NOT affected by this option. - WriteZeroValues bool - // Controls whether the @jsonName trait is used to // determine JSON object keys. If false (the default), the member // name is used as-is. @@ -59,86 +53,8 @@ func (s *ShapeSerializer) Bytes() []byte { return s.root.Bytes() } -// WriteInt8Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt8Ptr(schema *smithy.Schema, v *int8) { - if v != nil { - s.withWriteZero(func() { s.WriteInt8(schema, *v) }) - } -} - -// WriteInt16Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt16Ptr(schema *smithy.Schema, v *int16) { - if v != nil { - s.withWriteZero(func() { s.WriteInt16(schema, *v) }) - } -} - -// WriteInt32Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt32Ptr(schema *smithy.Schema, v *int32) { - if v != nil { - s.withWriteZero(func() { s.WriteInt32(schema, *v) }) - } -} - -// WriteInt64Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt64Ptr(schema *smithy.Schema, v *int64) { - if v != nil { - s.withWriteZero(func() { s.WriteInt64(schema, *v) }) - } -} - -// WriteFloat32Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteFloat32Ptr(schema *smithy.Schema, v *float32) { - if v != nil { - s.withWriteZero(func() { s.WriteFloat32(schema, *v) }) - } -} - -// WriteFloat64Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteFloat64Ptr(schema *smithy.Schema, v *float64) { - if v != nil { - s.withWriteZero(func() { s.WriteFloat64(schema, *v) }) - } -} - -// WriteBoolPtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteBoolPtr(schema *smithy.Schema, v *bool) { - if v != nil { - s.withWriteZero(func() { s.WriteBool(schema, *v) }) - } -} - -// WriteStringPtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteStringPtr(schema *smithy.Schema, v *string) { - if v != nil { - s.withWriteZero(func() { s.WriteString(schema, *v) }) - } -} - -func (s *ShapeSerializer) withWriteZero(fn func()) { - prev := s.opts.WriteZeroValues - s.opts.WriteZeroValues = true - fn() - s.opts.WriteZeroValues = prev -} - -func (s *ShapeSerializer) skipZeroValue() bool { - if s.opts.WriteZeroValues { - return false - } - switch s.head.Top().(type) { - case *smithyjson.Array, smithyjson.Value: - return false - default: - return true - } -} - // WriteBool implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { - if !v && s.skipZeroValue() { - return - } switch enc := s.head.Top().(type) { case *smithyjson.Object: enc.Key(s.jsonMemberName(schema)).Boolean(v) @@ -154,9 +70,6 @@ func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { // WriteInt8 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { - if v == 0 && s.skipZeroValue() { - return - } switch enc := s.head.Top().(type) { case *smithyjson.Object: enc.Key(s.jsonMemberName(schema)).Byte(v) @@ -172,9 +85,6 @@ func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { // WriteInt16 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { - if v == 0 && s.skipZeroValue() { - return - } switch enc := s.head.Top().(type) { case *smithyjson.Object: enc.Key(s.jsonMemberName(schema)).Short(v) @@ -190,9 +100,6 @@ func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { // WriteInt32 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { - if v == 0 && s.skipZeroValue() { - return - } switch enc := s.head.Top().(type) { case *smithyjson.Object: enc.Key(s.jsonMemberName(schema)).Integer(v) @@ -208,9 +115,6 @@ func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { // WriteInt64 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { - if v == 0 && s.skipZeroValue() { - return - } switch enc := s.head.Top().(type) { case *smithyjson.Object: enc.Key(s.jsonMemberName(schema)).Long(v) @@ -226,10 +130,6 @@ func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { // WriteFloat32 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { - if v == 0 && s.skipZeroValue() { - return - } - var jv smithyjson.Value switch enc := s.head.Top().(type) { case *smithyjson.Object: @@ -255,10 +155,6 @@ func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { } } func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { - if v == 0 && s.skipZeroValue() { - return - } - var jv smithyjson.Value switch enc := s.head.Top().(type) { case *smithyjson.Object: @@ -286,9 +182,6 @@ func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { // WriteString implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { - if v == "" && s.skipZeroValue() { - return - } switch enc := s.head.Top().(type) { case *smithyjson.Object: enc.Key(s.jsonMemberName(schema)).String(v) @@ -304,10 +197,6 @@ func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { // WriteBlob implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { - if v == nil { // we don't write null so skipZeroValue is irrelevant - return - } - switch enc := s.head.Top().(type) { case *smithyjson.Object: enc.Key(s.jsonMemberName(schema)).Base64EncodeBytes(v) @@ -398,13 +287,6 @@ func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { } } -// WriteTimePtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { - if v != nil { - s.WriteTime(schema, *v) - } -} - // WriteUnion implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema, v smithy.Serializable) { switch enc := s.head.Top().(type) { @@ -466,13 +348,13 @@ func (s *ShapeSerializer) WriteNil(schema *smithy.Schema) { } } -// WriteBigInteger is unimplemented and will panic. -func (s *ShapeSerializer) WriteBigInteger(schema *smithy.Schema, v big.Int) { +// WriteBigInt is unimplemented and will panic. +func (s *ShapeSerializer) WriteBigInt(schema *smithy.Schema, v *big.Int) { panic("unimplemented") } -// WriteBigDecimal is unimplemented and will panic. -func (s *ShapeSerializer) WriteBigDecimal(schema *smithy.Schema, v big.Float) { +// WriteBigFloat is unimplemented and will panic. +func (s *ShapeSerializer) WriteBigFloat(schema *smithy.Schema, v *big.Float) { panic("unimplemented") } diff --git a/aws-protocols/internal/query/shape_serializer.go b/aws-protocols/internal/query/shape_serializer.go index 84ef4bb50..6a91768cb 100644 --- a/aws-protocols/internal/query/shape_serializer.go +++ b/aws-protocols/internal/query/shape_serializer.go @@ -63,8 +63,6 @@ type kv struct { // ShapeSerializerOptions configures a ShapeSerializer. type ShapeSerializerOptions struct { - WriteZeroValues bool - // Adjusts serialization for the ec2Query protocol: // - Member names resolve via @ec2QueryName, then @xmlName // capitalized, then member name capitalized (instead of @xmlName @@ -118,29 +116,6 @@ func (s *ShapeSerializer) top() ctxKind { return t.kind } -func (s *ShapeSerializer) withWriteZero(fn func()) { - prev := s.opts.WriteZeroValues - s.opts.WriteZeroValues = true - fn() - s.opts.WriteZeroValues = prev -} - -func (s *ShapeSerializer) skipZeroValue() bool { - if s.opts.WriteZeroValues { - return false - } - if s.stack.Len() == 0 { - return false - } - - switch s.top() { - case ctxKindList, ctxKindMapValue: - return false - default: - return true - } -} - func (s *ShapeSerializer) memberName(schema *smithy.Schema) string { if s.opts.EC2Mode { return ec2MemberName(schema) @@ -214,60 +189,8 @@ func (s *ShapeSerializer) bufferMapEntry(key, value string) bool { return false } -// WriteInt8Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt8Ptr(schema *smithy.Schema, v *int8) { - writePtr(s, schema, v, (*ShapeSerializer).WriteInt8) -} - -// WriteInt16Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt16Ptr(schema *smithy.Schema, v *int16) { - writePtr(s, schema, v, (*ShapeSerializer).WriteInt16) -} - -// WriteInt32Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt32Ptr(schema *smithy.Schema, v *int32) { - writePtr(s, schema, v, (*ShapeSerializer).WriteInt32) -} - -// WriteInt64Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt64Ptr(schema *smithy.Schema, v *int64) { - writePtr(s, schema, v, (*ShapeSerializer).WriteInt64) -} - -// WriteFloat32Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteFloat32Ptr(schema *smithy.Schema, v *float32) { - writePtr(s, schema, v, (*ShapeSerializer).WriteFloat32) -} - -// WriteFloat64Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteFloat64Ptr(schema *smithy.Schema, v *float64) { - writePtr(s, schema, v, (*ShapeSerializer).WriteFloat64) -} - -// WriteBoolPtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteBoolPtr(schema *smithy.Schema, v *bool) { - writePtr(s, schema, v, (*ShapeSerializer).WriteBool) -} - -// WriteStringPtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteStringPtr(schema *smithy.Schema, v *string) { - writePtr(s, schema, v, (*ShapeSerializer).WriteString) -} - -func writePtr[T any](s *ShapeSerializer, schema *smithy.Schema, v *T, write func(*ShapeSerializer, *smithy.Schema, T)) { - if v == nil { - return - } - - s.withWriteZero(func() { write(s, schema, *v) }) -} - // WriteBool implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { - if !v && s.skipZeroValue() { - return - } - if v { s.writeValue(s.resolveKey(schema), "true") } else { @@ -294,18 +217,10 @@ func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { write func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { writeFloat(s, schema, v) } func writeInt[T int8 | int16 | int32 | int64](s *ShapeSerializer, schema *smithy.Schema, v T) { - if v == 0 && s.skipZeroValue() { - return - } - s.writeValue(s.resolveKey(schema), strconv.FormatInt(int64(v), 10)) } func writeFloat[T float32 | float64](s *ShapeSerializer, schema *smithy.Schema, v T) { - if v == 0 && s.skipZeroValue() { - return - } - s.writeValue(s.resolveKey(schema), formatFloat(float64(v))) } @@ -324,10 +239,6 @@ func formatFloat(v float64) string { // WriteString implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { - if v == "" && s.skipZeroValue() { - return - } - s.writeValue(s.resolveKey(schema), v) } @@ -362,13 +273,6 @@ func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { s.writeValue(s.resolveKey(schema), sv) } -// WriteTimePtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { - if v != nil { - s.WriteTime(schema, *v) - } -} - // WriteStruct implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema) { saved := s.currPrefix @@ -551,13 +455,13 @@ func (s *ShapeSerializer) CloseMap() { s.currPrefix = ctx.prefix } -// WriteBigInteger is unimplemented. -func (s *ShapeSerializer) WriteBigInteger(_ *smithy.Schema, _ big.Int) { +// WriteBigInt is unimplemented. +func (s *ShapeSerializer) WriteBigInt(_ *smithy.Schema, _ *big.Int) { panic("query: BigInteger not supported") } -// WriteBigDecimal is unimplemented. -func (s *ShapeSerializer) WriteBigDecimal(_ *smithy.Schema, _ big.Float) { +// WriteBigFloat is unimplemented. +func (s *ShapeSerializer) WriteBigFloat(_ *smithy.Schema, _ *big.Float) { panic("query: BigDecimal not supported") } diff --git a/aws-protocols/internal/xml/shape_serializer.go b/aws-protocols/internal/xml/shape_serializer.go index a35d91f61..bfbfed3c2 100644 --- a/aws-protocols/internal/xml/shape_serializer.go +++ b/aws-protocols/internal/xml/shape_serializer.go @@ -86,34 +86,6 @@ func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { s.writeScalar(schema, strconv.FormatInt(v, 10)) } -// WriteInt8Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt8Ptr(schema *smithy.Schema, v *int8) { - if v != nil { - s.WriteInt8(schema, *v) - } -} - -// WriteInt16Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt16Ptr(schema *smithy.Schema, v *int16) { - if v != nil { - s.WriteInt16(schema, *v) - } -} - -// WriteInt32Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt32Ptr(schema *smithy.Schema, v *int32) { - if v != nil { - s.WriteInt32(schema, *v) - } -} - -// WriteInt64Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt64Ptr(schema *smithy.Schema, v *int64) { - if v != nil { - s.WriteInt64(schema, *v) - } -} - // WriteFloat32 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { s.writeScalar(schema, formatFloat(float64(v), 32)) @@ -124,52 +96,23 @@ func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { s.writeScalar(schema, formatFloat(v, 64)) } -// WriteFloat32Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteFloat32Ptr(schema *smithy.Schema, v *float32) { - if v != nil { - s.WriteFloat32(schema, *v) - } -} - -// WriteFloat64Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteFloat64Ptr(schema *smithy.Schema, v *float64) { - if v != nil { - s.WriteFloat64(schema, *v) - } -} - // WriteBool implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { s.writeScalar(schema, strconv.FormatBool(v)) } -// WriteBoolPtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteBoolPtr(schema *smithy.Schema, v *bool) { - if v != nil { - s.WriteBool(schema, *v) - } -} - // WriteString implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { s.writeScalar(schema, v) } -// WriteStringPtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteStringPtr(schema *smithy.Schema, v *string) { - if v == nil { - return - } - s.WriteString(schema, *v) -} - -// WriteBigInteger is unimplemented. -func (s *ShapeSerializer) WriteBigInteger(_ *smithy.Schema, _ big.Int) { +// WriteBigInt is unimplemented. +func (s *ShapeSerializer) WriteBigInt(_ *smithy.Schema, _ *big.Int) { panic("BigInteger not supported") } -// WriteBigDecimal is unimplemented. -func (s *ShapeSerializer) WriteBigDecimal(_ *smithy.Schema, _ big.Float) { +// WriteBigFloat is unimplemented. +func (s *ShapeSerializer) WriteBigFloat(_ *smithy.Schema, _ *big.Float) { panic("BigDecimal not supported") } @@ -198,13 +141,6 @@ func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { } } -// WriteTimePtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { - if v != nil { - s.WriteTime(schema, *v) - } -} - // WriteStruct implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema) { name := s.structEname(schema) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java index e21bcfeba..36ca0a41c 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java @@ -39,8 +39,9 @@ public void accept(GoWriter writer) { private void renderDense(GoWriter writer) { writer.writeGoTemplate(""" func deserialize$shapeName:L(d smithy.ShapeDeserializer, s *smithy.Schema, v *$symbol:T) error { + var vv $memberSymbol:T return smithy.ReadList(d, s, func() error { - var vv $memberSymbol:T + $zeroValue:W if err := $deserializeMember:W; err != nil { return err } @@ -59,6 +60,7 @@ private void renderDense(GoWriter writer) { case DOCUMENT -> SmithyGoDependency.SMITHY_DOCUMENT.valueSymbol("Value"); default -> ctx.symbolProvider().toSymbol(member); }, + "zeroValue", renderZeroValue(), "deserializeMember", renderDeserializeMember(), "cast", renderDenseCast() )); @@ -67,6 +69,7 @@ private void renderDense(GoWriter writer) { private void renderSparse(GoWriter writer) { writer.writeGoTemplate(""" func deserialize$shapeName:L(d smithy.ShapeDeserializer, s *smithy.Schema, v *$symbol:T) error { + var vv $memberSymbol:T return smithy.ReadList(d, s, func() error { if isNil, err := d.ReadNil(s.ListMember()); err != nil { return err @@ -75,7 +78,7 @@ private void renderSparse(GoWriter writer) { return nil } - var vv $memberSymbol:T + $zeroValue:W if err := $deserializeMember:W; err != nil { return err } @@ -94,11 +97,20 @@ private void renderSparse(GoWriter writer) { case DOCUMENT -> SmithyGoDependency.SMITHY_DOCUMENT.valueSymbol("Value"); default -> ctx.symbolProvider().toSymbol(member); }, + "zeroValue", renderZeroValue(), "deserializeMember", renderDeserializeMember(), "cast", renderSparseCast() )); } + private Writable renderZeroValue() { + return switch (member.getType()) { + case STRUCTURE -> + goTemplate("vv = $T{}", ctx.symbolProvider().toSymbol(member)); + default -> goTemplate(""); + }; + } + private Writable renderSparseCast() { return switch (member.getType()) { case STRING, ENUM, INT_ENUM -> ShapeUtil.isEnum(member) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java index e32d839c9..6db1b7aec 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java @@ -40,8 +40,9 @@ private void renderDense(GoWriter writer) { writer.writeGoTemplate(""" func deserialize$shapeName:L(d smithy.ShapeDeserializer, s *smithy.Schema, v *$symbol:T) error { *v = make($symbol:T) + var vv $valueSymbol:T return smithy.ReadMap(d, s, func(k string) error { - var vv $valueSymbol:T + $zeroValue:W if err := $deserializeValue:W; err != nil { return err } @@ -60,6 +61,7 @@ private void renderDense(GoWriter writer) { case DOCUMENT -> SmithyGoDependency.SMITHY_DOCUMENT.valueSymbol("Value"); default -> ctx.symbolProvider().toSymbol(value); }, + "zeroValue", renderZeroValue(), "deserializeValue", renderDeserializeValue(), "cast", renderDenseCast() )); @@ -69,6 +71,7 @@ private void renderSparse(GoWriter writer) { writer.writeGoTemplate(""" func deserialize$shapeName:L(d smithy.ShapeDeserializer, s *smithy.Schema, v *$symbol:T) error { *v = make($symbol:T) + var vv $valueSymbol:T return smithy.ReadMap(d, s, func(k string) error { if isNil, err := d.ReadNil(s.MapValue()); err != nil { return err @@ -77,7 +80,7 @@ private void renderSparse(GoWriter writer) { return nil } - var vv $valueSymbol:T + $zeroValue:W if err := $deserializeValue:W; err != nil { return err } @@ -96,11 +99,20 @@ private void renderSparse(GoWriter writer) { case DOCUMENT -> SmithyGoDependency.SMITHY_DOCUMENT.valueSymbol("Value"); default -> ctx.symbolProvider().toSymbol(value); }, + "zeroValue", renderZeroValue(), "deserializeValue", renderDeserializeValue(), "cast", renderSparseCast() )); } + private Writable renderZeroValue() { + return switch (value.getType()) { + case STRUCTURE -> + goTemplate("vv = $T{}", ctx.symbolProvider().toSymbol(value)); + default -> goTemplate(""); + }; + } + private Writable renderSparseCast() { return switch (value.getType()) { case STRING, ENUM, INT_ENUM -> ShapeUtil.isEnum(value) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java index c1eed2684..e3cf71f0b 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java @@ -63,42 +63,42 @@ public void accept(GoWriter writer) { private void generateSerializeMember(GoWriter writer, MemberShape member, Shape target, String ident) { var schemaName = SchemaGenerator.getMemberSchemaRef(shape, member, ctx.service()); - var ptrSuffix = nilIndex.isNillable(member) ? "Ptr" : ""; + var isNillable = nilIndex.isNillable(member); switch (target.getType()) { case BYTE -> - writer.write("s.WriteInt8$L($L, $L)", ptrSuffix, schemaName, ident); + writeScalar(writer, isNillable, ident, "0", "WriteInt8", schemaName); case SHORT -> - writer.write("s.WriteInt16$L($L, $L)", ptrSuffix, schemaName, ident); + writeScalar(writer, isNillable, ident, "0", "WriteInt16", schemaName); case INTEGER -> - writer.write("s.WriteInt32$L($L, $L)", ptrSuffix, schemaName, ident); + writeScalar(writer, isNillable, ident, "0", "WriteInt32", schemaName); case LONG -> - writer.write("s.WriteInt64$L($L, $L)", ptrSuffix, schemaName, ident); + writeScalar(writer, isNillable, ident, "0", "WriteInt64", schemaName); case FLOAT -> - writer.write("s.WriteFloat32$L($L, $L)", ptrSuffix, schemaName, ident); + writeScalar(writer, isNillable, ident, "0", "WriteFloat32", schemaName); case DOUBLE -> - writer.write("s.WriteFloat64$L($L, $L)", ptrSuffix, schemaName, ident); + writeScalar(writer, isNillable, ident, "0", "WriteFloat64", schemaName); case STRING, ENUM -> { - if (ShapeUtil.isEnum(target)) { - writer.write("s.WriteString($L, string($L))", schemaName, ident); - } else { - writer.write("s.WriteString$L($L, $L)", ptrSuffix, schemaName, ident); - } + if (ShapeUtil.isEnum(target)) { + writer.write("if $1L != \"\" { s.WriteString($2L, string($1L)) }", ident, schemaName); + } else { + writeScalar(writer, isNillable, ident, "\"\"", "WriteString", schemaName); + } } case INT_ENUM -> - writer.write("s.WriteInt32($L, int32($L))", schemaName, ident); + writer.write("if $1L != 0 { s.WriteInt32($2L, int32($1L)) }", ident, schemaName); case BOOLEAN -> - writer.write("s.WriteBool$L($L, $L)", ptrSuffix, schemaName, ident); + writeScalar(writer, isNillable, ident, "false", "WriteBool", schemaName); case TIMESTAMP -> - writer.write("s.WriteTime$L($L, $L)", ptrSuffix, schemaName, ident); + writeScalar(writer, isNillable, ident, "", "WriteTime", schemaName); case BLOB -> writer.write("s.WriteBlob($L, $L)", schemaName, ident); case LIST, SET, MAP, UNION -> writer.write("serialize$L(s, $L, $L)", target.getId().getName(), schemaName, ident); case STRUCTURE -> - writer.write("if ($2L != nil) { s.WriteStruct($1L)\n$2L.SerializeMembers(s)\ns.CloseStruct() }", schemaName, ident); + writer.write("if $2L != nil { s.WriteStruct($1L)\n$2L.SerializeMembers(s)\ns.CloseStruct() }", schemaName, ident); case DOCUMENT -> { writer.addUseImports(SmithyGoDependency.SMITHY_DOCUMENT); writer.write("s.WriteDocument($L, &smithydocument.Opaque{Value: $L})", schemaName, ident); @@ -111,4 +111,20 @@ private void generateSerializeMember(GoWriter writer, MemberShape member, Shape case MEMBER, SERVICE, RESOURCE, OPERATION -> throw new CodegenException("invalid shape " + target.getType()); } } + + // Generates a nil-guarded (pointer) or zero-guarded (value) scalar write. + // writeMethod is e.g. "WriteInt32". schemaName is the schema ref. ident is + // the Go expression for the member value (e.g. "v.Foo"). + private void writeScalar(GoWriter writer, boolean isNillable, String ident, String zeroValue, + String writeMethod, String schemaName) { + if (isNillable) { + writer.write("if $1L != nil { s.$3L($2L, *$1L) }", ident, schemaName, writeMethod); + } else { + if (zeroValue.isEmpty()) { + writer.write("if !$1L.IsZero() { s.$3L($2L, $1L) }", ident, schemaName, writeMethod); + } else { + writer.write("if $1L != " + zeroValue + " { s.$3L($2L, $1L) }", ident, schemaName, writeMethod); + } + } + } } diff --git a/eventstream/serializer.go b/eventstream/serializer.go index bf51f727d..97e20172f 100644 --- a/eventstream/serializer.go +++ b/eventstream/serializer.go @@ -48,13 +48,6 @@ func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { s.inner.WriteBool(schema, v) } -// WriteBoolPtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteBoolPtr(schema *smithy.Schema, v *bool) { - if v != nil { - s.WriteBool(schema, *v) - } -} - // WriteInt8 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { if isEventHeader(schema) { @@ -64,13 +57,6 @@ func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { s.inner.WriteInt8(schema, v) } -// WriteInt8Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt8Ptr(schema *smithy.Schema, v *int8) { - if v != nil { - s.WriteInt8(schema, *v) - } -} - // WriteInt16 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { if isEventHeader(schema) { @@ -80,13 +66,6 @@ func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { s.inner.WriteInt16(schema, v) } -// WriteInt16Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt16Ptr(schema *smithy.Schema, v *int16) { - if v != nil { - s.WriteInt16(schema, *v) - } -} - // WriteInt32 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { if isEventHeader(schema) { @@ -96,13 +75,6 @@ func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { s.inner.WriteInt32(schema, v) } -// WriteInt32Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt32Ptr(schema *smithy.Schema, v *int32) { - if v != nil { - s.WriteInt32(schema, *v) - } -} - // WriteInt64 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { if isEventHeader(schema) { @@ -112,33 +84,16 @@ func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { s.inner.WriteInt64(schema, v) } -// WriteInt64Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt64Ptr(schema *smithy.Schema, v *int64) { - if v != nil { - s.WriteInt64(schema, *v) - } -} - // WriteFloat32 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { s.inner.WriteFloat32(schema, v) } -// WriteFloat32Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteFloat32Ptr(schema *smithy.Schema, v *float32) { - s.inner.WriteFloat32Ptr(schema, v) -} - // WriteFloat64 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { s.inner.WriteFloat64(schema, v) } -// WriteFloat64Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteFloat64Ptr(schema *smithy.Schema, v *float64) { - s.inner.WriteFloat64Ptr(schema, v) -} - // WriteString implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { if isEventHeader(schema) { @@ -153,13 +108,6 @@ func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { s.inner.WriteString(schema, v) } -// WriteStringPtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteStringPtr(schema *smithy.Schema, v *string) { - if v != nil { - s.WriteString(schema, *v) - } -} - // WriteBlob implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { if isEventHeader(schema) { @@ -183,11 +131,14 @@ func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { s.inner.WriteTime(schema, v) } -// WriteTimePtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { - if v != nil { - s.WriteTime(schema, *v) - } +// WriteBigInt implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBigInt(schema *smithy.Schema, v *big.Int) { + s.inner.WriteBigInt(schema, v) +} + +// WriteBigFloat implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) WriteBigFloat(schema *smithy.Schema, v *big.Float) { + s.inner.WriteBigFloat(schema, v) } // WriteStruct implements [smithy.ShapeSerializer]. @@ -233,16 +184,6 @@ func (s *ShapeSerializer) CloseMap() { s.inner.CloseMap() } -// WriteBigInteger implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteBigInteger(schema *smithy.Schema, v big.Int) { - s.inner.WriteBigInteger(schema, v) -} - -// WriteBigDecimal implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteBigDecimal(schema *smithy.Schema, v big.Float) { - s.inner.WriteBigDecimal(schema, v) -} - // WriteDocument implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteDocument(schema *smithy.Schema, v document.Value) { s.inner.WriteDocument(schema, v) diff --git a/serde.go b/serde.go index 59468b2b6..418ff84cb 100644 --- a/serde.go +++ b/serde.go @@ -12,6 +12,19 @@ import ( // ShapeSerializer implements the marshaling of an in-code representation of a // shape to an unspecified data format, which is determined by the // implementation. +// +// A ShapeSerializer is consumed by the **code-generated** Serialize() method +// of a modeled structure. For example: +// +// func (v *PutItemInput) Serialize(s smithy.ShapeSerializer) { +// s.WriteStruct(schemas.PutItemInput) +// v.SerializeMembers(s) +// s.CloseStruct() +// } +// +// func (v *PutItemInput) SerializeMembers(s smithy.ShapeSerializer) { +// // TODO(serde2) +// } type ShapeSerializer interface { Bytes() []byte @@ -19,43 +32,28 @@ type ShapeSerializer interface { WriteInt16(*Schema, int16) WriteInt32(*Schema, int32) WriteInt64(*Schema, int64) - WriteInt8Ptr(*Schema, *int8) - WriteInt16Ptr(*Schema, *int16) - WriteInt32Ptr(*Schema, *int32) - WriteInt64Ptr(*Schema, *int64) - WriteFloat32(*Schema, float32) WriteFloat64(*Schema, float64) - WriteFloat32Ptr(*Schema, *float32) - WriteFloat64Ptr(*Schema, *float64) - WriteBool(*Schema, bool) - WriteBoolPtr(*Schema, *bool) - WriteString(*Schema, string) - WriteStringPtr(*Schema, *string) - - WriteBigInteger(*Schema, big.Int) - WriteBigDecimal(*Schema, big.Float) + WriteBigInt(*Schema, *big.Int) + WriteBigFloat(*Schema, *big.Float) WriteBlob(*Schema, []byte) WriteTime(*Schema, time.Time) - WriteTimePtr(*Schema, *time.Time) - - WriteStruct(*Schema) - CloseStruct() WriteUnion(schema, variant *Schema, v Serializable) - + WriteDocument(*Schema, document.Value) WriteNil(*Schema) + WriteStruct(*Schema) + CloseStruct() + WriteList(*Schema) CloseList() WriteMap(*Schema) WriteKey(*Schema, string) CloseMap() - - WriteDocument(*Schema, document.Value) } // ShapeDeserializer implements the unmarshaling from some unspecified data diff --git a/smithy-http-protocols/internal/cbor/shape_serializer.go b/smithy-http-protocols/internal/cbor/shape_serializer.go index d619a58be..0e7b2b284 100644 --- a/smithy-http-protocols/internal/cbor/shape_serializer.go +++ b/smithy-http-protocols/internal/cbor/shape_serializer.go @@ -24,9 +24,7 @@ type ShapeSerializer struct { } // ShapeSerializerOptions configures ShapeSerializer. -type ShapeSerializerOptions struct { - WriteZeroValues bool -} +type ShapeSerializerOptions struct{} var _ smithy.ShapeSerializer = (*ShapeSerializer)(nil) @@ -69,89 +67,8 @@ func (s *ShapeSerializer) pop() { s.head = s.head[:len(s.head)-1] } -// WriteInt8Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt8Ptr(schema *smithy.Schema, v *int8) { - if v != nil { - s.withWriteZero(func() { s.WriteInt8(schema, *v) }) - } -} - -// WriteInt16Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt16Ptr(schema *smithy.Schema, v *int16) { - if v != nil { - s.withWriteZero(func() { s.WriteInt16(schema, *v) }) - } -} - -// WriteInt32Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt32Ptr(schema *smithy.Schema, v *int32) { - if v != nil { - s.withWriteZero(func() { s.WriteInt32(schema, *v) }) - } -} - -// WriteInt64Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteInt64Ptr(schema *smithy.Schema, v *int64) { - if v != nil { - s.withWriteZero(func() { s.WriteInt64(schema, *v) }) - } -} - -// WriteFloat32Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteFloat32Ptr(schema *smithy.Schema, v *float32) { - if v != nil { - s.withWriteZero(func() { s.WriteFloat32(schema, *v) }) - } -} - -// WriteFloat64Ptr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteFloat64Ptr(schema *smithy.Schema, v *float64) { - if v != nil { - s.withWriteZero(func() { s.WriteFloat64(schema, *v) }) - } -} - -// WriteBoolPtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteBoolPtr(schema *smithy.Schema, v *bool) { - if v != nil { - s.withWriteZero(func() { s.WriteBool(schema, *v) }) - } -} - -// WriteStringPtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteStringPtr(schema *smithy.Schema, v *string) { - if v != nil { - s.withWriteZero(func() { s.WriteString(schema, *v) }) - } -} - -func (s *ShapeSerializer) withWriteZero(fn func()) { - prev := s.opts.WriteZeroValues - s.opts.WriteZeroValues = true - fn() - s.opts.WriteZeroValues = prev -} - -func (s *ShapeSerializer) skipZeroValue() bool { - if s.opts.WriteZeroValues { - return false - } - if len(s.head) == 0 { - return false - } - switch s.top() { - case ctxList, ctxMapValue: - return false - default: - return true - } -} - // WriteBool implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteBool(schema *smithy.Schema, v bool) { - if !v && s.skipZeroValue() { - return - } s.writeKey(schema) if v { s.buf = append(s.buf, compose(majorType7, major7True)) @@ -174,45 +91,30 @@ func (s *ShapeSerializer) writeUint(v uint64) { // WriteInt8 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt8(schema *smithy.Schema, v int8) { - if v == 0 && s.skipZeroValue() { - return - } s.writeKey(schema) s.writeInt(int64(v)) } // WriteInt16 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt16(schema *smithy.Schema, v int16) { - if v == 0 && s.skipZeroValue() { - return - } s.writeKey(schema) s.writeInt(int64(v)) } // WriteInt32 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt32(schema *smithy.Schema, v int32) { - if v == 0 && s.skipZeroValue() { - return - } s.writeKey(schema) s.writeInt(int64(v)) } // WriteInt64 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteInt64(schema *smithy.Schema, v int64) { - if v == 0 && s.skipZeroValue() { - return - } s.writeKey(schema) s.writeInt(v) } // WriteFloat32 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { - if v == 0 && !math.Signbit(float64(v)) && s.skipZeroValue() { - return - } s.writeKey(schema) s.buf = append(s.buf, compose(majorType7, major7Float32)) s.buf = binary.BigEndian.AppendUint32(s.buf, math.Float32bits(v)) @@ -220,9 +122,6 @@ func (s *ShapeSerializer) WriteFloat32(schema *smithy.Schema, v float32) { // WriteFloat64 implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { - if v == 0 && !math.Signbit(v) && s.skipZeroValue() { - return - } s.writeKey(schema) s.buf = append(s.buf, compose(majorType7, major7Float64)) s.buf = binary.BigEndian.AppendUint64(s.buf, math.Float64bits(v)) @@ -230,9 +129,6 @@ func (s *ShapeSerializer) WriteFloat64(schema *smithy.Schema, v float64) { // WriteString implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteString(schema *smithy.Schema, v string) { - if v == "" && s.skipZeroValue() { - return - } s.writeKey(schema) s.writeTextString(v) } @@ -308,13 +204,6 @@ func (s *ShapeSerializer) WriteTime(schema *smithy.Schema, v time.Time) { s.buf = binary.BigEndian.AppendUint64(s.buf, math.Float64bits(epoch)) } -// WriteTimePtr implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteTimePtr(schema *smithy.Schema, v *time.Time) { - if v != nil { - s.WriteTime(schema, *v) - } -} - // WriteUnion implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema, v smithy.Serializable) { s.writeKey(schema) @@ -352,13 +241,13 @@ func (s *ShapeSerializer) WriteNil(schema *smithy.Schema) { s.buf = append(s.buf, compose(majorType7, major7Nil)) } -// WriteBigInteger is unimplemented and will panic. -func (s *ShapeSerializer) WriteBigInteger(schema *smithy.Schema, v big.Int) { +// WriteBigInt is unimplemented and will panic. +func (s *ShapeSerializer) WriteBigInt(schema *smithy.Schema, v *big.Int) { panic("unimplemented") } -// WriteBigDecimal is unimplemented and will panic. -func (s *ShapeSerializer) WriteBigDecimal(schema *smithy.Schema, v big.Float) { +// WriteBigFloat is unimplemented and will panic. +func (s *ShapeSerializer) WriteBigFloat(schema *smithy.Schema, v *big.Float) { panic("unimplemented") } From 55f76324c56e00d70aed9d858a2ebae4806b78ed Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Mon, 18 May 2026 17:11:00 -0400 Subject: [PATCH 22/38] serde2: json parser (#660) --- aws-protocols/awsjson/awsjson.go | 2 +- aws-protocols/internal/json/benchmark_test.go | 686 ++++++++++++++++++ aws-protocols/internal/json/fuzz_test.go | 59 ++ .../internal/json/internal/stdlib/LICENSE | 27 + .../internal/json/internal/stdlib/stdlib.go | 144 ++++ .../internal/json/jsontestsuite_test.go | 90 +++ aws-protocols/internal/json/parser.go | 242 ++++++ aws-protocols/internal/json/scanner.go | 191 +++++ aws-protocols/internal/json/scanner_test.go | 557 ++++++++++++++ .../internal/json/shape_deserializer.go | 408 +++++------ .../internal/json/shape_serializer.go | 32 +- aws-protocols/internal/json/stack.go | 38 + .../internal/json/testdata/README.md | 1 + .../i_number_double_huge_neg_exp.json | 1 + .../test_parsing/i_number_huge_exp.json | 1 + .../i_number_neg_int_huge_exp.json | 1 + .../i_number_pos_double_huge_exp.json | 1 + .../i_number_real_neg_overflow.json | 1 + .../i_number_real_pos_overflow.json | 1 + .../test_parsing/i_number_real_underflow.json | 1 + .../i_number_too_big_neg_int.json | 1 + .../i_number_too_big_pos_int.json | 1 + .../i_number_very_big_negative_int.json | 1 + .../i_object_key_lone_2nd_surrogate.json | 1 + ..._string_1st_surrogate_but_2nd_missing.json | 1 + ...tring_1st_valid_surrogate_2nd_invalid.json | 1 + .../i_string_UTF-16LE_with_BOM.json | Bin 0 -> 12 bytes .../i_string_UTF-8_invalid_sequence.json | 1 + .../i_string_UTF8_surrogate_U+D800.json | 1 + ...incomplete_surrogate_and_escape_valid.json | 1 + .../i_string_incomplete_surrogate_pair.json | 1 + ...ng_incomplete_surrogates_escape_valid.json | 1 + .../i_string_invalid_lonely_surrogate.json | 1 + .../i_string_invalid_surrogate.json | 1 + .../test_parsing/i_string_invalid_utf-8.json | 1 + .../i_string_inverted_surrogates_U+1D11E.json | 1 + .../test_parsing/i_string_iso_latin_1.json | 1 + .../i_string_lone_second_surrogate.json | 1 + .../i_string_lone_utf8_continuation_byte.json | 1 + .../i_string_not_in_unicode_range.json | 1 + .../i_string_overlong_sequence_2_bytes.json | 1 + .../i_string_overlong_sequence_6_bytes.json | 1 + ...string_overlong_sequence_6_bytes_null.json | 1 + .../i_string_truncated-utf-8.json | 1 + .../test_parsing/i_string_utf16BE_no_BOM.json | Bin 0 -> 10 bytes .../test_parsing/i_string_utf16LE_no_BOM.json | Bin 0 -> 10 bytes .../i_structure_500_nested_arrays.json | 1 + .../i_structure_UTF-8_BOM_empty_object.json | 1 + .../n_array_1_true_without_comma.json | 1 + .../test_parsing/n_array_a_invalid_utf8.json | 1 + .../n_array_colon_instead_of_comma.json | 1 + .../n_array_comma_after_close.json | 1 + .../n_array_comma_and_number.json | 1 + .../test_parsing/n_array_double_comma.json | 1 + .../n_array_double_extra_comma.json | 1 + .../test_parsing/n_array_extra_close.json | 1 + .../test_parsing/n_array_extra_comma.json | 1 + .../test_parsing/n_array_incomplete.json | 1 + .../n_array_incomplete_invalid_value.json | 1 + .../n_array_inner_array_no_comma.json | 1 + .../test_parsing/n_array_invalid_utf8.json | 1 + .../n_array_items_separated_by_semicolon.json | 1 + .../test_parsing/n_array_just_comma.json | 1 + .../test_parsing/n_array_just_minus.json | 1 + .../test_parsing/n_array_missing_value.json | 1 + .../n_array_newlines_unclosed.json | 3 + .../n_array_number_and_comma.json | 1 + .../n_array_number_and_several_commas.json | 1 + .../n_array_spaces_vertical_tab_formfeed.json | 1 + .../test_parsing/n_array_star_inside.json | 1 + .../test_parsing/n_array_unclosed.json | 1 + .../n_array_unclosed_trailing_comma.json | 1 + .../n_array_unclosed_with_new_lines.json | 3 + .../n_array_unclosed_with_object_inside.json | 1 + .../test_parsing/n_incomplete_false.json | 1 + .../test_parsing/n_incomplete_null.json | 1 + .../test_parsing/n_incomplete_true.json | 1 + .../n_multidigit_number_then_00.json | Bin 0 -> 4 bytes .../testdata/test_parsing/n_number_++.json | 1 + .../testdata/test_parsing/n_number_+1.json | 1 + .../testdata/test_parsing/n_number_+Inf.json | 1 + .../testdata/test_parsing/n_number_-01.json | 1 + .../testdata/test_parsing/n_number_-1.0..json | 1 + .../testdata/test_parsing/n_number_-2..json | 1 + .../testdata/test_parsing/n_number_-NaN.json | 1 + .../testdata/test_parsing/n_number_.-1.json | 1 + .../testdata/test_parsing/n_number_.2e-3.json | 1 + .../testdata/test_parsing/n_number_0.1.2.json | 1 + .../testdata/test_parsing/n_number_0.3e+.json | 1 + .../testdata/test_parsing/n_number_0.3e.json | 1 + .../testdata/test_parsing/n_number_0.e1.json | 1 + .../test_parsing/n_number_0_capital_E+.json | 1 + .../test_parsing/n_number_0_capital_E.json | 1 + .../testdata/test_parsing/n_number_0e+.json | 1 + .../testdata/test_parsing/n_number_0e.json | 1 + .../testdata/test_parsing/n_number_1.0e+.json | 1 + .../testdata/test_parsing/n_number_1.0e-.json | 1 + .../testdata/test_parsing/n_number_1.0e.json | 1 + .../testdata/test_parsing/n_number_1_000.json | 1 + .../testdata/test_parsing/n_number_1eE2.json | 1 + .../testdata/test_parsing/n_number_2.e+3.json | 1 + .../testdata/test_parsing/n_number_2.e-3.json | 1 + .../testdata/test_parsing/n_number_2.e3.json | 1 + .../testdata/test_parsing/n_number_9.e+.json | 1 + .../testdata/test_parsing/n_number_Inf.json | 1 + .../testdata/test_parsing/n_number_NaN.json | 1 + .../n_number_U+FF11_fullwidth_digit_one.json | 1 + .../test_parsing/n_number_expression.json | 1 + .../test_parsing/n_number_hex_1_digit.json | 1 + .../test_parsing/n_number_hex_2_digits.json | 1 + .../test_parsing/n_number_infinity.json | 1 + .../test_parsing/n_number_invalid+-.json | 1 + .../n_number_invalid-negative-real.json | 1 + .../n_number_invalid-utf-8-in-bigger-int.json | 1 + .../n_number_invalid-utf-8-in-exponent.json | 1 + .../n_number_invalid-utf-8-in-int.json | 1 + .../test_parsing/n_number_minus_infinity.json | 1 + ...mber_minus_sign_with_trailing_garbage.json | 1 + .../test_parsing/n_number_minus_space_1.json | 1 + .../n_number_neg_int_starting_with_zero.json | 1 + .../n_number_neg_real_without_int_part.json | 1 + .../n_number_neg_with_garbage_at_end.json | 1 + .../n_number_real_garbage_after_e.json | 1 + ...number_real_with_invalid_utf8_after_e.json | 1 + ...n_number_real_without_fractional_part.json | 1 + .../n_number_starting_with_dot.json | 1 + .../test_parsing/n_number_with_alpha.json | 1 + .../n_number_with_alpha_char.json | 1 + .../n_number_with_leading_zero.json | 1 + .../test_parsing/n_object_bad_value.json | 1 + .../test_parsing/n_object_bracket_key.json | 1 + .../n_object_comma_instead_of_colon.json | 1 + .../test_parsing/n_object_double_colon.json | 1 + .../testdata/test_parsing/n_object_emoji.json | 1 + .../test_parsing/n_object_garbage_at_end.json | 1 + .../n_object_key_with_single_quotes.json | 1 + ...uation_byte_in_key_and_trailing_comma.json | 1 + .../test_parsing/n_object_missing_colon.json | 1 + .../test_parsing/n_object_missing_key.json | 1 + .../n_object_missing_semicolon.json | 1 + .../test_parsing/n_object_missing_value.json | 1 + .../test_parsing/n_object_no-colon.json | 1 + .../test_parsing/n_object_non_string_key.json | 1 + ...on_string_key_but_huge_number_instead.json | 1 + .../n_object_repeated_null_null.json | 1 + .../n_object_several_trailing_commas.json | 1 + .../test_parsing/n_object_single_quote.json | 1 + .../test_parsing/n_object_trailing_comma.json | 1 + .../n_object_trailing_comment.json | 1 + .../n_object_trailing_comment_open.json | 1 + .../n_object_trailing_comment_slash_open.json | 1 + ...railing_comment_slash_open_incomplete.json | 1 + .../n_object_two_commas_in_a_row.json | 1 + .../test_parsing/n_object_unquoted_key.json | 1 + .../n_object_unterminated-value.json | 1 + .../n_object_with_single_string.json | 1 + .../n_object_with_trailing_garbage.json | 1 + .../testdata/test_parsing/n_single_space.json | 1 + .../n_string_1_surrogate_then_escape.json | 1 + .../n_string_1_surrogate_then_escape_u.json | 1 + .../n_string_1_surrogate_then_escape_u1.json | 1 + .../n_string_1_surrogate_then_escape_u1x.json | 1 + .../n_string_accentuated_char_no_quotes.json | 1 + .../test_parsing/n_string_backslash_00.json | Bin 0 -> 6 bytes .../test_parsing/n_string_escape_x.json | 1 + .../n_string_escaped_backslash_bad.json | 1 + .../n_string_escaped_ctrl_char_tab.json | 1 + .../test_parsing/n_string_escaped_emoji.json | 1 + .../n_string_incomplete_escape.json | 1 + ...n_string_incomplete_escaped_character.json | 1 + .../n_string_incomplete_surrogate.json | 1 + ...g_incomplete_surrogate_escape_invalid.json | 1 + .../n_string_invalid-utf-8-in-escape.json | 1 + .../n_string_invalid_backslash_esc.json | 1 + .../n_string_invalid_unicode_escape.json | 1 + .../n_string_invalid_utf8_after_escape.json | 1 + .../n_string_leading_uescaped_thinspace.json | 1 + .../n_string_no_quotes_with_bad_escape.json | 1 + .../n_string_single_doublequote.json | 1 + .../test_parsing/n_string_single_quote.json | 1 + ...string_single_string_no_double_quotes.json | 1 + .../n_string_start_escape_unclosed.json | 1 + .../n_string_unescaped_ctrl_char.json | Bin 0 -> 7 bytes .../n_string_unescaped_newline.json | 2 + .../test_parsing/n_string_unescaped_tab.json | 1 + .../n_string_unicode_CapitalU.json | 1 + .../n_string_with_trailing_garbage.json | 1 + .../n_structure_100000_opening_arrays.json | 1 + .../n_structure_U+2060_word_joined.json | 1 + .../n_structure_UTF8_BOM_no_data.json | 1 + .../n_structure_angle_bracket_..json | 1 + .../n_structure_angle_bracket_null.json | 1 + .../n_structure_array_trailing_garbage.json | 1 + ...tructure_array_with_extra_array_close.json | 1 + ..._structure_array_with_unclosed_string.json | 1 + .../n_structure_ascii-unicode-identifier.json | 1 + .../n_structure_capitalized_True.json | 1 + .../n_structure_close_unopened_array.json | 1 + ...ucture_comma_instead_of_closing_brace.json | 1 + .../n_structure_double_array.json | 1 + .../test_parsing/n_structure_end_array.json | 1 + .../n_structure_incomplete_UTF8_BOM.json | 1 + .../n_structure_lone-invalid-utf-8.json | 1 + .../n_structure_lone-open-bracket.json | 1 + .../test_parsing/n_structure_no_data.json | 0 .../n_structure_null-byte-outside-string.json | Bin 0 -> 3 bytes ...tructure_number_with_trailing_garbage.json | 1 + ...ure_object_followed_by_closing_object.json | 1 + .../n_structure_object_unclosed_no_value.json | 1 + .../n_structure_object_with_comment.json | 1 + ...tructure_object_with_trailing_garbage.json | 1 + .../n_structure_open_array_apostrophe.json | 1 + .../n_structure_open_array_comma.json | 1 + .../n_structure_open_array_object.json | 1 + .../n_structure_open_array_open_object.json | 1 + .../n_structure_open_array_open_string.json | 1 + .../n_structure_open_array_string.json | 1 + .../test_parsing/n_structure_open_object.json | 1 + .../n_structure_open_object_close_array.json | 1 + .../n_structure_open_object_comma.json | 1 + .../n_structure_open_object_open_array.json | 1 + .../n_structure_open_object_open_string.json | 1 + ...e_open_object_string_with_apostrophes.json | 1 + .../test_parsing/n_structure_open_open.json | 1 + .../n_structure_single_eacute.json | 1 + .../test_parsing/n_structure_single_star.json | 1 + .../test_parsing/n_structure_trailing_#.json | 1 + ...n_structure_uescaped_LF_before_string.json | 1 + .../n_structure_unclosed_array.json | 1 + ...structure_unclosed_array_partial_null.json | 1 + ...cture_unclosed_array_unfinished_false.json | 1 + ...ucture_unclosed_array_unfinished_true.json | 1 + .../n_structure_unclosed_object.json | 1 + .../n_structure_unicode-identifier.json | 1 + ...ructure_whitespace_U+2060_word_joiner.json | 1 + .../n_structure_whitespace_formfeed.json | 1 + .../y_array_arraysWithSpaces.json | 1 + .../test_parsing/y_array_empty-string.json | 1 + .../testdata/test_parsing/y_array_empty.json | 1 + .../y_array_ending_with_newline.json | 1 + .../testdata/test_parsing/y_array_false.json | 1 + .../test_parsing/y_array_heterogeneous.json | 1 + .../testdata/test_parsing/y_array_null.json | 1 + .../y_array_with_1_and_newline.json | 2 + .../y_array_with_leading_space.json | 1 + .../y_array_with_several_null.json | 1 + .../y_array_with_trailing_space.json | 1 + .../json/testdata/test_parsing/y_number.json | 1 + .../testdata/test_parsing/y_number_0e+1.json | 1 + .../testdata/test_parsing/y_number_0e1.json | 1 + .../test_parsing/y_number_after_space.json | 1 + .../y_number_double_close_to_zero.json | 1 + .../test_parsing/y_number_int_with_exp.json | 1 + .../test_parsing/y_number_minus_zero.json | 1 + .../test_parsing/y_number_negative_int.json | 1 + .../test_parsing/y_number_negative_one.json | 1 + .../test_parsing/y_number_negative_zero.json | 1 + .../test_parsing/y_number_real_capital_e.json | 1 + .../y_number_real_capital_e_neg_exp.json | 1 + .../y_number_real_capital_e_pos_exp.json | 1 + .../test_parsing/y_number_real_exponent.json | 1 + .../y_number_real_fraction_exponent.json | 1 + .../test_parsing/y_number_real_neg_exp.json | 1 + .../y_number_real_pos_exponent.json | 1 + .../test_parsing/y_number_simple_int.json | 1 + .../test_parsing/y_number_simple_real.json | 1 + .../json/testdata/test_parsing/y_object.json | 1 + .../testdata/test_parsing/y_object_basic.json | 1 + .../test_parsing/y_object_duplicated_key.json | 1 + .../y_object_duplicated_key_and_value.json | 1 + .../testdata/test_parsing/y_object_empty.json | 1 + .../test_parsing/y_object_empty_key.json | 1 + .../y_object_escaped_null_in_key.json | 1 + .../y_object_extreme_numbers.json | 1 + .../test_parsing/y_object_long_strings.json | 1 + .../test_parsing/y_object_simple.json | 1 + .../test_parsing/y_object_string_unicode.json | 1 + .../test_parsing/y_object_with_newlines.json | 3 + .../y_string_1_2_3_bytes_UTF-8_sequences.json | 1 + .../y_string_accepted_surrogate_pair.json | 1 + .../y_string_accepted_surrogate_pairs.json | 1 + .../y_string_allowed_escapes.json | 1 + ...y_string_backslash_and_u_escaped_zero.json | 1 + .../y_string_backslash_doublequotes.json | 1 + .../test_parsing/y_string_comments.json | 1 + .../y_string_double_escape_a.json | 1 + .../y_string_double_escape_n.json | 1 + .../y_string_escaped_control_character.json | 1 + .../y_string_escaped_noncharacter.json | 1 + .../test_parsing/y_string_in_array.json | 1 + .../y_string_in_array_with_leading_space.json | 1 + .../y_string_last_surrogates_1_and_2.json | 1 + .../test_parsing/y_string_nbsp_uescaped.json | 1 + ...y_string_nonCharacterInUTF-8_U+10FFFF.json | 1 + .../y_string_nonCharacterInUTF-8_U+FFFF.json | 1 + .../test_parsing/y_string_null_escape.json | 1 + .../test_parsing/y_string_one-byte-utf-8.json | 1 + .../testdata/test_parsing/y_string_pi.json | 1 + ...ring_reservedCharacterInUTF-8_U+1BFFF.json | 1 + .../test_parsing/y_string_simple_ascii.json | 1 + .../testdata/test_parsing/y_string_space.json | 1 + ...rogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json | 1 + .../y_string_three-byte-utf-8.json | 1 + .../test_parsing/y_string_two-byte-utf-8.json | 1 + .../y_string_u+2028_line_sep.json | 1 + .../test_parsing/y_string_u+2029_par_sep.json | 1 + .../test_parsing/y_string_uEscape.json | 1 + .../y_string_uescaped_newline.json | 1 + .../y_string_unescaped_char_delete.json | 1 + .../test_parsing/y_string_unicode.json | 1 + .../y_string_unicodeEscapedBackslash.json | 1 + .../test_parsing/y_string_unicode_2.json | 1 + .../y_string_unicode_U+10FFFE_nonchar.json | 1 + .../y_string_unicode_U+1FFFE_nonchar.json | 1 + ...tring_unicode_U+200B_ZERO_WIDTH_SPACE.json | 1 + ..._string_unicode_U+2064_invisible_plus.json | 1 + .../y_string_unicode_U+FDD0_nonchar.json | 1 + .../y_string_unicode_U+FFFE_nonchar.json | 1 + ...y_string_unicode_escaped_double_quote.json | 1 + .../testdata/test_parsing/y_string_utf8.json | 1 + .../y_string_with_del_character.json | 1 + .../y_structure_lonely_false.json | 1 + .../test_parsing/y_structure_lonely_int.json | 1 + .../y_structure_lonely_negative_real.json | 1 + .../test_parsing/y_structure_lonely_null.json | 1 + .../y_structure_lonely_string.json | 1 + .../test_parsing/y_structure_lonely_true.json | 1 + .../y_structure_string_empty.json | 1 + .../y_structure_trailing_newline.json | 1 + .../y_structure_true_in_array.json | 1 + .../y_structure_whitespace_array.json | 1 + 331 files changed, 2542 insertions(+), 253 deletions(-) create mode 100644 aws-protocols/internal/json/benchmark_test.go create mode 100644 aws-protocols/internal/json/fuzz_test.go create mode 100644 aws-protocols/internal/json/internal/stdlib/LICENSE create mode 100644 aws-protocols/internal/json/internal/stdlib/stdlib.go create mode 100644 aws-protocols/internal/json/jsontestsuite_test.go create mode 100644 aws-protocols/internal/json/parser.go create mode 100644 aws-protocols/internal/json/scanner.go create mode 100644 aws-protocols/internal/json/scanner_test.go create mode 100644 aws-protocols/internal/json/stack.go create mode 100644 aws-protocols/internal/json/testdata/README.md create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_number_double_huge_neg_exp.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_number_huge_exp.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/i_number_neg_int_huge_exp.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/i_number_pos_double_huge_exp.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_number_real_neg_overflow.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_number_real_pos_overflow.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_number_real_underflow.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_number_too_big_neg_int.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_number_too_big_pos_int.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/i_number_very_big_negative_int.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_object_key_lone_2nd_surrogate.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_string_1st_surrogate_but_2nd_missing.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_string_1st_valid_surrogate_2nd_invalid.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_string_UTF-16LE_with_BOM.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/i_string_UTF-8_invalid_sequence.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_string_UTF8_surrogate_U+D800.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/i_string_incomplete_surrogate_and_escape_valid.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/i_string_incomplete_surrogate_pair.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/i_string_incomplete_surrogates_escape_valid.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/i_string_invalid_lonely_surrogate.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/i_string_invalid_surrogate.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_string_invalid_utf-8.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/i_string_inverted_surrogates_U+1D11E.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_string_iso_latin_1.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_string_lone_second_surrogate.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_string_lone_utf8_continuation_byte.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_string_not_in_unicode_range.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_string_overlong_sequence_2_bytes.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/i_string_overlong_sequence_6_bytes.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/i_string_overlong_sequence_6_bytes_null.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_string_truncated-utf-8.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_string_utf16BE_no_BOM.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_string_utf16LE_no_BOM.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/i_structure_500_nested_arrays.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/i_structure_UTF-8_BOM_empty_object.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_1_true_without_comma.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_a_invalid_utf8.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_colon_instead_of_comma.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_comma_after_close.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_array_comma_and_number.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_array_double_comma.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_double_extra_comma.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_extra_close.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_extra_comma.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_incomplete.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_incomplete_invalid_value.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_inner_array_no_comma.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_invalid_utf8.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_array_items_separated_by_semicolon.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_array_just_comma.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_array_just_minus.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_missing_value.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_newlines_unclosed.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_array_number_and_comma.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_array_number_and_several_commas.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_array_spaces_vertical_tab_formfeed.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_array_star_inside.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed_trailing_comma.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed_with_new_lines.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed_with_object_inside.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_incomplete_false.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_incomplete_null.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_incomplete_true.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_multidigit_number_then_00.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_++.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_+1.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_+Inf.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_-01.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_-1.0..json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_-2..json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_-NaN.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_.-1.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_.2e-3.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_0.1.2.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_0.3e+.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_0.3e.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_0.e1.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_0_capital_E+.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_0_capital_E.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_0e+.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_0e.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_1.0e+.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_1.0e-.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_1.0e.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_1_000.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_1eE2.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_2.e+3.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_2.e-3.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_2.e3.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_9.e+.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_Inf.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_NaN.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_U+FF11_fullwidth_digit_one.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_expression.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_hex_1_digit.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_hex_2_digits.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_infinity.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_invalid+-.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-negative-real.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-utf-8-in-bigger-int.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-utf-8-in-exponent.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-utf-8-in-int.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_minus_infinity.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_minus_sign_with_trailing_garbage.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_minus_space_1.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_neg_int_starting_with_zero.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_neg_real_without_int_part.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_neg_with_garbage_at_end.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_real_garbage_after_e.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_real_with_invalid_utf8_after_e.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_real_without_fractional_part.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_starting_with_dot.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_with_alpha.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_number_with_alpha_char.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_number_with_leading_zero.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_bad_value.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_bracket_key.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_comma_instead_of_colon.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_double_colon.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_emoji.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_garbage_at_end.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_object_key_with_single_quotes.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_missing_colon.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_object_missing_key.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_object_missing_semicolon.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_missing_value.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_no-colon.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_object_non_string_key.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_object_non_string_key_but_huge_number_instead.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_object_repeated_null_null.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_object_several_trailing_commas.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_single_quote.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comma.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment_open.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment_slash_open.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment_slash_open_incomplete.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_object_two_commas_in_a_row.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_unquoted_key.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_unterminated-value.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_with_single_string.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_object_with_trailing_garbage.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_single_space.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape_u.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape_u1.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape_u1x.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_accentuated_char_no_quotes.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_backslash_00.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_escape_x.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_string_escaped_backslash_bad.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_escaped_ctrl_char_tab.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_escaped_emoji.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_escape.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_escaped_character.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_surrogate.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_surrogate_escape_invalid.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_invalid-utf-8-in-escape.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_string_invalid_backslash_esc.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_invalid_unicode_escape.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_invalid_utf8_after_escape.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_string_leading_uescaped_thinspace.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_no_quotes_with_bad_escape.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_string_single_doublequote.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_single_quote.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_string_single_string_no_double_quotes.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_start_escape_unclosed.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_string_unescaped_ctrl_char.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_unescaped_newline.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_unescaped_tab.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_unicode_CapitalU.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_string_with_trailing_garbage.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_100000_opening_arrays.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_U+2060_word_joined.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_UTF8_BOM_no_data.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_angle_bracket_..json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_angle_bracket_null.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_array_trailing_garbage.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_array_with_extra_array_close.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_array_with_unclosed_string.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_ascii-unicode-identifier.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_capitalized_True.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_close_unopened_array.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_comma_instead_of_closing_brace.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_double_array.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_end_array.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_incomplete_UTF8_BOM.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_lone-invalid-utf-8.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_lone-open-bracket.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_no_data.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_null-byte-outside-string.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_number_with_trailing_garbage.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_object_followed_by_closing_object.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_object_unclosed_no_value.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_object_with_comment.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_object_with_trailing_garbage.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_apostrophe.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_comma.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_object.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_open_object.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_open_string.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_string.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_close_array.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_comma.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_open_array.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_open_string.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_string_with_apostrophes.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_open_open.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_single_eacute.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_single_star.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_trailing_#.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_uescaped_LF_before_string.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array_partial_null.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array_unfinished_false.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array_unfinished_true.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_object.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/n_structure_unicode-identifier.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_whitespace_U+2060_word_joiner.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/n_structure_whitespace_formfeed.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_array_arraysWithSpaces.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_array_empty-string.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_array_empty.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_array_ending_with_newline.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_array_false.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_array_heterogeneous.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_array_null.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_array_with_1_and_newline.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_array_with_leading_space.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_array_with_several_null.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_array_with_trailing_space.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_number_0e+1.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_number_0e1.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number_after_space.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_number_double_close_to_zero.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_number_int_with_exp.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_number_minus_zero.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number_negative_int.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number_negative_one.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number_negative_zero.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number_real_capital_e.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number_real_capital_e_neg_exp.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number_real_capital_e_pos_exp.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number_real_exponent.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number_real_fraction_exponent.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number_real_neg_exp.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number_real_pos_exponent.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number_simple_int.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_number_simple_real.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_object.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_object_basic.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_object_duplicated_key.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_object_duplicated_key_and_value.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_object_empty.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_object_empty_key.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_object_escaped_null_in_key.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_object_extreme_numbers.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_object_long_strings.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_object_simple.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_object_string_unicode.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_object_with_newlines.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_1_2_3_bytes_UTF-8_sequences.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_accepted_surrogate_pair.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_accepted_surrogate_pairs.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_allowed_escapes.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_backslash_and_u_escaped_zero.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_backslash_doublequotes.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_comments.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_double_escape_a.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_double_escape_n.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_escaped_control_character.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_escaped_noncharacter.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_in_array.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_in_array_with_leading_space.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_last_surrogates_1_and_2.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_nbsp_uescaped.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_nonCharacterInUTF-8_U+FFFF.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_null_escape.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_one-byte-utf-8.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_pi.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_reservedCharacterInUTF-8_U+1BFFF.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_simple_ascii.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_space.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_three-byte-utf-8.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_two-byte-utf-8.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_u+2028_line_sep.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_u+2029_par_sep.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_uEscape.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_uescaped_newline.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_unescaped_char_delete.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_unicode.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_unicodeEscapedBackslash.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_2.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+10FFFE_nonchar.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+1FFFE_nonchar.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+2064_invisible_plus.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+FDD0_nonchar.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+FFFE_nonchar.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_escaped_double_quote.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_string_utf8.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_string_with_del_character.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_false.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_int.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_negative_real.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_null.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_string.json create mode 100755 aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_true.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_structure_string_empty.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_structure_trailing_newline.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_structure_true_in_array.json create mode 100644 aws-protocols/internal/json/testdata/test_parsing/y_structure_whitespace_array.json diff --git a/aws-protocols/awsjson/awsjson.go b/aws-protocols/awsjson/awsjson.go index e3a6115e7..8021df40e 100644 --- a/aws-protocols/awsjson/awsjson.go +++ b/aws-protocols/awsjson/awsjson.go @@ -10,8 +10,8 @@ import ( "github.com/aws/smithy-go" internaljson "github.com/aws/smithy-go/aws-protocols/internal/json" - internales "github.com/aws/smithy-go/internal/eventstream" internalerrors "github.com/aws/smithy-go/internal/errors" + internales "github.com/aws/smithy-go/internal/eventstream" smithyio "github.com/aws/smithy-go/io" "github.com/aws/smithy-go/middleware" "github.com/aws/smithy-go/traits" diff --git a/aws-protocols/internal/json/benchmark_test.go b/aws-protocols/internal/json/benchmark_test.go new file mode 100644 index 000000000..92dd355b2 --- /dev/null +++ b/aws-protocols/internal/json/benchmark_test.go @@ -0,0 +1,686 @@ +package json + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "testing" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/prelude" +) + +// Benchmark comparing old (stdlib json.Decoder + tree walk) vs new (fastjson +// ShapeDeserializer) for DynamoDB AttributeValue deserialization. +// +// The payload is a GetItem response with a realistic mix of attribute types. + +// --- test payload --- + +var benchPayload = []byte(`{ + "ConsumedCapacity": { + "TableName": "Users", + "CapacityUnits": 0.5 + }, + "Item": { + "pk": {"S": "user#12345"}, + "sk": {"S": "profile"}, + "name": {"S": "Jane Doe"}, + "age": {"N": "34"}, + "verified": {"BOOL": true}, + "email": {"S": "jane@example.com"}, + "scores": {"NS": ["98", "76", "100", "88"]}, + "tags": {"SS": ["admin", "premium", "early-adopter"]}, + "avatar": {"B": "` + base64.StdEncoding.EncodeToString([]byte("fake-image-bytes-here")) + `"}, + "metadata": {"M": { + "created": {"S": "2024-01-15T10:30:00Z"}, + "source": {"S": "web"}, + "loginCount": {"N": "142"}, + "preferences": {"M": { + "theme": {"S": "dark"}, + "notifications": {"BOOL": true} + }} + }}, + "history": {"L": [ + {"M": {"action": {"S": "login"}, "ts": {"N": "1700000000"}}}, + {"M": {"action": {"S": "purchase"}, "ts": {"N": "1700100000"}}}, + {"M": {"action": {"S": "logout"}, "ts": {"N": "1700200000"}}} + ]}, + "nothing": {"NULL": true} + } +}`) + +// --- types (mirrors DynamoDB SDK types) --- + +type AttributeValue interface{ isAttributeValue() } + +type AttributeValueMemberS struct{ Value string } +type AttributeValueMemberN struct{ Value string } +type AttributeValueMemberB struct{ Value []byte } +type AttributeValueMemberBOOL struct{ Value bool } +type AttributeValueMemberNULL struct{ Value bool } +type AttributeValueMemberSS struct{ Value []string } +type AttributeValueMemberNS struct{ Value []string } +type AttributeValueMemberBS struct{ Value [][]byte } +type AttributeValueMemberL struct{ Value []AttributeValue } +type AttributeValueMemberM struct{ Value map[string]AttributeValue } + +func (*AttributeValueMemberS) isAttributeValue() {} +func (*AttributeValueMemberN) isAttributeValue() {} +func (*AttributeValueMemberB) isAttributeValue() {} +func (*AttributeValueMemberBOOL) isAttributeValue() {} +func (*AttributeValueMemberNULL) isAttributeValue() {} +func (*AttributeValueMemberSS) isAttributeValue() {} +func (*AttributeValueMemberNS) isAttributeValue() {} +func (*AttributeValueMemberBS) isAttributeValue() {} +func (*AttributeValueMemberL) isAttributeValue() {} +func (*AttributeValueMemberM) isAttributeValue() {} + +type ConsumedCapacity struct { + TableName *string + CapacityUnits float64 +} + +type GetItemOutput struct { + ConsumedCapacity *ConsumedCapacity + Item map[string]AttributeValue +} + +// ========================================================================== +// OLD PATH: json.Decoder -> interface{} -> tree walk +// ========================================================================== + +func oldDeserialize(data []byte) (*GetItemOutput, error) { + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.UseNumber() + var shape interface{} + if err := decoder.Decode(&shape); err != nil && err != io.EOF { + return nil, err + } + + var out GetItemOutput + if err := oldDeserializeGetItemOutput(&out, shape); err != nil { + return nil, err + } + return &out, nil +} + +func oldDeserializeGetItemOutput(v *GetItemOutput, value interface{}) error { + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + for key, value := range shape { + switch key { + case "ConsumedCapacity": + if err := oldDeserializeConsumedCapacity(&v.ConsumedCapacity, value); err != nil { + return err + } + case "Item": + if err := oldDeserializeAttributeMap(&v.Item, value); err != nil { + return err + } + } + } + return nil +} + +func oldDeserializeConsumedCapacity(v **ConsumedCapacity, value interface{}) error { + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + sv := &ConsumedCapacity{} + for key, value := range shape { + switch key { + case "TableName": + if value != nil { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected string, got %T", value) + } + sv.TableName = &jtv + } + case "CapacityUnits": + if value != nil { + jtv, ok := value.(json.Number) + if !ok { + return fmt.Errorf("expected number, got %T", value) + } + f, err := jtv.Float64() + if err != nil { + return err + } + sv.CapacityUnits = f + } + } + } + *v = sv + return nil +} + +func oldDeserializeAttributeMap(v *map[string]AttributeValue, value interface{}) error { + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + mv := make(map[string]AttributeValue, len(shape)) + for key, value := range shape { + var parsedVal AttributeValue + if err := oldDeserializeAttributeValue(&parsedVal, value); err != nil { + return err + } + mv[key] = parsedVal + } + *v = mv + return nil +} + +func oldDeserializeAttributeValue(v *AttributeValue, value interface{}) error { + if value == nil { + return nil + } + + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("unexpected JSON type %v", value) + } + + for key, value := range shape { + if value == nil { + continue + } + switch key { + case "S": + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected string, got %T", value) + } + *v = &AttributeValueMemberS{Value: jtv} + return nil + case "N": + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected string, got %T", value) + } + *v = &AttributeValueMemberN{Value: jtv} + return nil + case "B": + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected string, got %T", value) + } + dv, err := base64.StdEncoding.DecodeString(jtv) + if err != nil { + return err + } + *v = &AttributeValueMemberB{Value: dv} + return nil + case "BOOL": + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected bool, got %T", value) + } + *v = &AttributeValueMemberBOOL{Value: jtv} + return nil + case "NULL": + jtv, ok := value.(bool) + if !ok { + return fmt.Errorf("expected bool, got %T", value) + } + *v = &AttributeValueMemberNULL{Value: jtv} + return nil + case "SS": + if err := oldDeserializeStringSet(v, value); err != nil { + return err + } + return nil + case "NS": + if err := oldDeserializeNumberSet(v, value); err != nil { + return err + } + return nil + case "BS": + if err := oldDeserializeBinarySet(v, value); err != nil { + return err + } + return nil + case "L": + if err := oldDeserializeList(v, value); err != nil { + return err + } + return nil + case "M": + if err := oldDeserializeMap(v, value); err != nil { + return err + } + return nil + } + } + return nil +} + +func oldDeserializeStringSet(v *AttributeValue, value interface{}) error { + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("expected list, got %T", value) + } + + cv := make([]string, 0, len(shape)) + for _, value := range shape { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected string, got %T", value) + } + cv = append(cv, jtv) + } + *v = &AttributeValueMemberSS{Value: cv} + return nil +} + +func oldDeserializeNumberSet(v *AttributeValue, value interface{}) error { + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("expected list, got %T", value) + } + + cv := make([]string, 0, len(shape)) + for _, value := range shape { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected string, got %T", value) + } + cv = append(cv, jtv) + } + *v = &AttributeValueMemberNS{Value: cv} + return nil +} + +func oldDeserializeBinarySet(v *AttributeValue, value interface{}) error { + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("expected list, got %T", value) + } + + cv := make([][]byte, 0, len(shape)) + for _, value := range shape { + jtv, ok := value.(string) + if !ok { + return fmt.Errorf("expected string, got %T", value) + } + dv, err := base64.StdEncoding.DecodeString(jtv) + if err != nil { + return err + } + cv = append(cv, dv) + } + *v = &AttributeValueMemberBS{Value: cv} + return nil +} + +func oldDeserializeList(v *AttributeValue, value interface{}) error { + shape, ok := value.([]interface{}) + if !ok { + return fmt.Errorf("expected list, got %T", value) + } + + cv := make([]AttributeValue, 0, len(shape)) + for _, value := range shape { + var col AttributeValue + if err := oldDeserializeAttributeValue(&col, value); err != nil { + return err + } + cv = append(cv, col) + } + *v = &AttributeValueMemberL{Value: cv} + return nil +} + +func oldDeserializeMap(v *AttributeValue, value interface{}) error { + shape, ok := value.(map[string]interface{}) + if !ok { + return fmt.Errorf("expected map, got %T", value) + } + + mv := make(map[string]AttributeValue, len(shape)) + for key, value := range shape { + var parsedVal AttributeValue + if err := oldDeserializeAttributeValue(&parsedVal, value); err != nil { + return err + } + mv[key] = parsedVal + } + *v = &AttributeValueMemberM{Value: mv} + return nil +} + +// ========================================================================== +// NEW PATH: fastjson ShapeDeserializer + schemas +// ========================================================================== + +// --- schemas --- + +var ( + schemaGetItemOutput = smithy.NewSchema(smithy.ShapeID{ + Namespace: "com.amazonaws.dynamodb", Name: "GetItemOutput", + }, smithy.ShapeTypeStructure, 2) + + schemaConsumedCapacity = smithy.NewSchema(smithy.ShapeID{ + Namespace: "com.amazonaws.dynamodb", Name: "ConsumedCapacity", + }, smithy.ShapeTypeStructure, 2) + + schemaAttributeValue = smithy.NewSchema(smithy.ShapeID{ + Namespace: "com.amazonaws.dynamodb", Name: "AttributeValue", + }, smithy.ShapeTypeUnion, 10) + + schemaAttributeMap = smithy.NewSchema(smithy.ShapeID{ + Namespace: "com.amazonaws.dynamodb", Name: "AttributeMap", + }, smithy.ShapeTypeMap, 0) + + schemaListAttributeValue = smithy.NewSchema(smithy.ShapeID{ + Namespace: "com.amazonaws.dynamodb", Name: "ListAttributeValue", + }, smithy.ShapeTypeList, 0) + + schemaMapAttributeValue = smithy.NewSchema(smithy.ShapeID{ + Namespace: "com.amazonaws.dynamodb", Name: "MapAttributeValue", + }, smithy.ShapeTypeMap, 0) + + schemaStringSetAttributeValue = smithy.NewSchema(smithy.ShapeID{ + Namespace: "com.amazonaws.dynamodb", Name: "StringSetAttributeValue", + }, smithy.ShapeTypeList, 0) + + schemaNumberSetAttributeValue = smithy.NewSchema(smithy.ShapeID{ + Namespace: "com.amazonaws.dynamodb", Name: "NumberSetAttributeValue", + }, smithy.ShapeTypeList, 0) + + schemaBinarySetAttributeValue = smithy.NewSchema(smithy.ShapeID{ + Namespace: "com.amazonaws.dynamodb", Name: "BinarySetAttributeValue", + }, smithy.ShapeTypeList, 0) + + // member schemas (populated in init) + schemaGetItemOutput_ConsumedCapacity *smithy.Schema + schemaGetItemOutput_Item *smithy.Schema + schemaConsumedCapacity_TableName *smithy.Schema + schemaConsumedCapacity_CapacityUnits *smithy.Schema + schemaAttributeValue_S *smithy.Schema + schemaAttributeValue_N *smithy.Schema + schemaAttributeValue_B *smithy.Schema + schemaAttributeValue_BOOL *smithy.Schema + schemaAttributeValue_NULL *smithy.Schema + schemaAttributeValue_SS *smithy.Schema + schemaAttributeValue_NS *smithy.Schema + schemaAttributeValue_BS *smithy.Schema + schemaAttributeValue_L *smithy.Schema + schemaAttributeValue_M *smithy.Schema +) + +func init() { + schemaGetItemOutput_ConsumedCapacity = schemaGetItemOutput.AddMember("ConsumedCapacity", schemaConsumedCapacity) + schemaGetItemOutput_Item = schemaGetItemOutput.AddMember("Item", schemaAttributeMap) + + schemaConsumedCapacity_TableName = schemaConsumedCapacity.AddMember("TableName", prelude.String) + schemaConsumedCapacity_CapacityUnits = schemaConsumedCapacity.AddMember("CapacityUnits", prelude.Double) + + schemaAttributeValue_S = schemaAttributeValue.AddMember("S", prelude.String) + schemaAttributeValue_N = schemaAttributeValue.AddMember("N", prelude.String) + schemaAttributeValue_B = schemaAttributeValue.AddMember("B", prelude.Blob) + schemaAttributeValue_BOOL = schemaAttributeValue.AddMember("BOOL", prelude.Boolean) + schemaAttributeValue_NULL = schemaAttributeValue.AddMember("NULL", prelude.Boolean) + schemaAttributeValue_SS = schemaAttributeValue.AddMember("SS", schemaStringSetAttributeValue) + schemaAttributeValue_NS = schemaAttributeValue.AddMember("NS", schemaNumberSetAttributeValue) + schemaAttributeValue_BS = schemaAttributeValue.AddMember("BS", schemaBinarySetAttributeValue) + schemaAttributeValue_L = schemaAttributeValue.AddMember("L", schemaListAttributeValue) + schemaAttributeValue_M = schemaAttributeValue.AddMember("M", schemaMapAttributeValue) + + schemaAttributeMap.AddMember("key", prelude.String) + schemaAttributeMap.AddMember("value", schemaAttributeValue) + + schemaListAttributeValue.AddMember("member", schemaAttributeValue) + + schemaMapAttributeValue.AddMember("key", prelude.String) + schemaMapAttributeValue.AddMember("value", schemaAttributeValue) + + schemaStringSetAttributeValue.AddMember("member", prelude.String) + schemaNumberSetAttributeValue.AddMember("member", prelude.String) + schemaBinarySetAttributeValue.AddMember("member", prelude.Blob) +} + +// --- new deserializers --- + +func newDeserialize(data []byte) (*GetItemOutput, error) { + d := NewShapeDeserializer(data) + var out GetItemOutput + if err := newDeserializeGetItemOutput(d, &out); err != nil { + return nil, err + } + return &out, nil +} + +func newDeserializeGetItemOutput(d smithy.ShapeDeserializer, v *GetItemOutput) error { + return smithy.ReadStruct(d, schemaGetItemOutput, func(ms *smithy.Schema) error { + switch ms { + case schemaGetItemOutput_ConsumedCapacity: + v.ConsumedCapacity = &ConsumedCapacity{} + return newDeserializeConsumedCapacity(d, v.ConsumedCapacity) + case schemaGetItemOutput_Item: + return newDeserializeAttributeMap(d, &v.Item) + } + return nil + }) +} + +func newDeserializeConsumedCapacity(d smithy.ShapeDeserializer, v *ConsumedCapacity) error { + return smithy.ReadStruct(d, schemaConsumedCapacity, func(ms *smithy.Schema) error { + switch ms { + case schemaConsumedCapacity_TableName: + return d.ReadStringPtr(schemaConsumedCapacity_TableName, &v.TableName) + case schemaConsumedCapacity_CapacityUnits: + return d.ReadFloat64(schemaConsumedCapacity_CapacityUnits, &v.CapacityUnits) + } + return nil + }) +} + +func newDeserializeAttributeMap(d smithy.ShapeDeserializer, v *map[string]AttributeValue) error { + return smithy.ReadMap(d, schemaAttributeMap, func(key string) error { + var av AttributeValue + if err := newDeserializeAttributeValue(d, &av); err != nil { + return err + } + if *v == nil { + *v = map[string]AttributeValue{} + } + (*v)[key] = av + return nil + }) +} + +func newDeserializeAttributeValue(d smithy.ShapeDeserializer, v *AttributeValue) error { + return smithy.ReadUnion(d, schemaAttributeValue, func(ms *smithy.Schema) error { + switch ms { + case schemaAttributeValue_S: + vv := &AttributeValueMemberS{} + *v = vv + return d.ReadString(schemaAttributeValue_S, &vv.Value) + case schemaAttributeValue_N: + vv := &AttributeValueMemberN{} + *v = vv + return d.ReadString(schemaAttributeValue_N, &vv.Value) + case schemaAttributeValue_B: + vv := &AttributeValueMemberB{} + *v = vv + return d.ReadBlob(schemaAttributeValue_B, &vv.Value) + case schemaAttributeValue_BOOL: + vv := &AttributeValueMemberBOOL{} + *v = vv + return d.ReadBool(schemaAttributeValue_BOOL, &vv.Value) + case schemaAttributeValue_NULL: + vv := &AttributeValueMemberNULL{} + *v = vv + return d.ReadBool(schemaAttributeValue_NULL, &vv.Value) + case schemaAttributeValue_SS: + vv := &AttributeValueMemberSS{} + *v = vv + return smithy.ReadList(d, schemaStringSetAttributeValue, func() error { + var s string + if err := d.ReadString(nil, &s); err != nil { + return err + } + vv.Value = append(vv.Value, s) + return nil + }) + case schemaAttributeValue_NS: + vv := &AttributeValueMemberNS{} + *v = vv + return smithy.ReadList(d, schemaNumberSetAttributeValue, func() error { + var s string + if err := d.ReadString(nil, &s); err != nil { + return err + } + vv.Value = append(vv.Value, s) + return nil + }) + case schemaAttributeValue_BS: + vv := &AttributeValueMemberBS{} + *v = vv + return smithy.ReadList(d, schemaBinarySetAttributeValue, func() error { + var b []byte + if err := d.ReadBlob(nil, &b); err != nil { + return err + } + vv.Value = append(vv.Value, b) + return nil + }) + case schemaAttributeValue_L: + vv := &AttributeValueMemberL{} + *v = vv + return smithy.ReadList(d, schemaListAttributeValue, func() error { + var av AttributeValue + if err := newDeserializeAttributeValue(d, &av); err != nil { + return err + } + vv.Value = append(vv.Value, av) + return nil + }) + case schemaAttributeValue_M: + vv := &AttributeValueMemberM{} + *v = vv + return smithy.ReadMap(d, schemaMapAttributeValue, func(key string) error { + var av AttributeValue + if err := newDeserializeAttributeValue(d, &av); err != nil { + return err + } + if vv.Value == nil { + vv.Value = map[string]AttributeValue{} + } + vv.Value[key] = av + return nil + }) + } + return nil + }) +} + +// ========================================================================== +// Benchmarks +// ========================================================================== + +func BenchmarkDeserialize_Old(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + out, err := oldDeserialize(benchPayload) + if err != nil { + b.Fatal(err) + } + if out.Item == nil { + b.Fatal("nil item") + } + } +} + +func BenchmarkDeserialize_New(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + out, err := newDeserialize(benchPayload) + if err != nil { + b.Fatal(err) + } + if out.Item == nil { + b.Fatal("nil item") + } + } +} + +// ========================================================================== +// Large payload +// ========================================================================== + +func generateLargePayload(targetSize int) []byte { + var buf bytes.Buffer + buf.WriteString(`{"ConsumedCapacity":{"TableName":"Bench","CapacityUnits":1.0},"Item":{`) + i := 0 + for buf.Len() < targetSize { + if i > 0 { + buf.WriteByte(',') + } + key := fmt.Sprintf("attr_%d", i) + switch i % 7 { + case 0: + fmt.Fprintf(&buf, `"%s":{"S":"value-%d-padding-to-add-some-length-here"}`, key, i) + case 1: + fmt.Fprintf(&buf, `"%s":{"N":"%d"}`, key, i*100) + case 2: + fmt.Fprintf(&buf, `"%s":{"BOOL":%t}`, key, i%2 == 0) + case 3: + fmt.Fprintf(&buf, `"%s":{"SS":["alpha%d","bravo%d","charlie%d","delta%d"]}`, key, i, i, i, i) + case 4: + fmt.Fprintf(&buf, `"%s":{"NS":["%d","%d","%d","%d"]}`, key, i, i+1, i+2, i+3) + case 5: + fmt.Fprintf(&buf, `"%s":{"M":{"nested":{"S":"v%d"},"count":{"N":"%d"},"flag":{"BOOL":true}}}`, key, i, i) + case 6: + fmt.Fprintf(&buf, `"%s":{"L":[{"S":"item%d"},{"N":"%d"},{"BOOL":true},{"NULL":true}]}`, key, i, i) + } + i++ + } + buf.WriteString(`}}`) + return buf.Bytes() +} + +var largePayload = generateLargePayload(512 * 1024 * 1024) + +func BenchmarkLargePayload_Old(b *testing.B) { + b.SetBytes(int64(len(largePayload))) + b.ReportAllocs() + for b.Loop() { + if _, err := oldDeserialize(largePayload); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkLargePayload_New(b *testing.B) { + b.SetBytes(int64(len(largePayload))) + b.ReportAllocs() + for b.Loop() { + if _, err := newDeserialize(largePayload); err != nil { + b.Fatal(err) + } + } +} diff --git a/aws-protocols/internal/json/fuzz_test.go b/aws-protocols/internal/json/fuzz_test.go new file mode 100644 index 000000000..a0dc157cb --- /dev/null +++ b/aws-protocols/internal/json/fuzz_test.go @@ -0,0 +1,59 @@ +package json + +import ( + "io" + "testing" +) + +// Adapted from the Go 1.24 standard library's encoding/json FuzzDecoderToken. +// +// https://cs.opensource.google/go/go/+/refs/tags/go1.24.0:src/encoding/json/fuzz_test.go +func FuzzParser(f *testing.F) { + // Seed corpus from stdlib FuzzDecoderToken. + f.Add([]byte(`{ +"object": { + "slice": [ + 1, + 2.0, + "3", + [4], + {5: {}} + ] +}, +"slice": [[]], +"string": ":)", +"int": 1e5, +"float": 3e-9" +}`)) + + // Additional seeds exercising edge cases. + f.Add([]byte(`null`)) + f.Add([]byte(`true`)) + f.Add([]byte(`false`)) + f.Add([]byte(`0`)) + f.Add([]byte(`""`)) + f.Add([]byte(`"\u0000"`)) + f.Add([]byte(`"\uD834\uDD1E"`)) + f.Add([]byte(`{}`)) + f.Add([]byte(`[]`)) + f.Add([]byte(`{"a":1,"b":[2,3],"c":{"d":true}}`)) + f.Add([]byte(`[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]`)) + f.Add([]byte(`"\/""`)) + f.Add([]byte(`-1.23e+45`)) + + f.Fuzz(func(t *testing.T, b []byte) { + p := parser{ + tok: scanner{p: b}, + parse: (*parser).parseValue, + } + for { + _, err := p.Next() + if err != nil { + if err == io.EOF { + break + } + return + } + } + }) +} diff --git a/aws-protocols/internal/json/internal/stdlib/LICENSE b/aws-protocols/internal/json/internal/stdlib/LICENSE new file mode 100644 index 000000000..6a66aea5e --- /dev/null +++ b/aws-protocols/internal/json/internal/stdlib/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/aws-protocols/internal/json/internal/stdlib/stdlib.go b/aws-protocols/internal/json/internal/stdlib/stdlib.go new file mode 100644 index 000000000..5d1a49ca5 --- /dev/null +++ b/aws-protocols/internal/json/internal/stdlib/stdlib.go @@ -0,0 +1,144 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package stdlib contains code copied from the Go standard library's +// encoding/json package (Go 1.24). +// +// We cannot use strconv.Unquote because JSON string rules differ from Go, +// JSON allows \/ and uses UTF-16 surrogate pairs. +// +// Source: https://cs.opensource.google/go/go/+/refs/tags/go1.24.0:src/encoding/json/decode.go +package stdlib + +import ( + "unicode" + "unicode/utf16" + "unicode/utf8" +) + +// Getu4 is copied from Go stdlib. +func Getu4(s []byte) rune { + if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { + return -1 + } + var r rune + for _, c := range s[2:6] { + switch { + case '0' <= c && c <= '9': + c = c - '0' + case 'a' <= c && c <= 'f': + c = c - 'a' + 10 + case 'A' <= c && c <= 'F': + c = c - 'A' + 10 + default: + return -1 + } + r = r*16 + rune(c) + } + return r +} + +// UnquoteBytes is copied from Go stdlib. +func UnquoteBytes(s []byte) (t []byte, ok bool) { + if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { + return + } + s = s[1 : len(s)-1] + + r := 0 + for r < len(s) { + c := s[r] + if c == '\\' || c == '"' || c < ' ' { + break + } + if c < utf8.RuneSelf { + r++ + continue + } + rr, size := utf8.DecodeRune(s[r:]) + if rr == utf8.RuneError && size == 1 { + break + } + r += size + } + if r == len(s) { + return s, true + } + + b := make([]byte, len(s)+2*utf8.UTFMax) + w := copy(b, s[0:r]) + for r < len(s) { + if w >= len(b)-2*utf8.UTFMax { + nb := make([]byte, (len(b)+utf8.UTFMax)*2) + copy(nb, b[0:w]) + b = nb + } + switch c := s[r]; { + case c == '\\': + r++ + if r >= len(s) { + return + } + switch s[r] { + default: + return + case '"', '\\', '/', '\'': + b[w] = s[r] + r++ + w++ + case 'b': + b[w] = '\b' + r++ + w++ + case 'f': + b[w] = '\f' + r++ + w++ + case 'n': + b[w] = '\n' + r++ + w++ + case 'r': + b[w] = '\r' + r++ + w++ + case 't': + b[w] = '\t' + r++ + w++ + case 'u': + r-- + rr := Getu4(s[r:]) + if rr < 0 { + return + } + r += 6 + if utf16.IsSurrogate(rr) { + rr1 := Getu4(s[r:]) + if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { + r += 6 + w += utf8.EncodeRune(b[w:], dec) + break + } + rr = unicode.ReplacementChar + } + w += utf8.EncodeRune(b[w:], rr) + } + + case c == '"', c < ' ': + return + + case c < utf8.RuneSelf: + b[w] = c + r++ + w++ + + default: + rr, size := utf8.DecodeRune(s[r:]) + r += size + w += utf8.EncodeRune(b[w:], rr) + } + } + return b[0:w], true +} diff --git a/aws-protocols/internal/json/jsontestsuite_test.go b/aws-protocols/internal/json/jsontestsuite_test.go new file mode 100644 index 000000000..059d0df52 --- /dev/null +++ b/aws-protocols/internal/json/jsontestsuite_test.go @@ -0,0 +1,90 @@ +package json + +import ( + "bytes" + "encoding/json" + "os" + "path/filepath" + "strings" + "testing" +) + +var skipTests = map[string]bool{ + // Trailing content after a valid top-level value, like stdlib, we parse + // one complete value and stop. + "n_array_comma_after_close.json": true, + "n_array_extra_close.json": true, + "n_multidigit_number_then_00.json": true, + "n_object_trailing_comment.json": true, + "n_object_trailing_comment_open.json": true, + "n_object_trailing_comment_slash_open.json": true, + "n_object_trailing_comment_slash_open_incomplete.json": true, + "n_object_with_trailing_garbage.json": true, + "n_string_with_trailing_garbage.json": true, + "n_structure_array_trailing_garbage.json": true, + "n_structure_array_with_extra_array_close.json": true, + "n_structure_close_unopened_array.json": true, + "n_structure_double_array.json": true, + "n_structure_number_with_trailing_garbage.json": true, + "n_structure_object_followed_by_closing_object.json": true, + "n_structure_object_with_trailing_garbage.json": true, + "n_structure_trailing_#.json": true, + + // These numbers have valid JSON grammar but overflow float64, stdlib + // rejects them at tokenize, we reject in the ShapeDeserializer. + "i_number_huge_exp.json": true, + "i_number_neg_int_huge_exp.json": true, + "i_number_pos_double_huge_exp.json": true, + "i_number_real_neg_overflow.json": true, + "i_number_real_pos_overflow.json": true, +} + +// Run test cases from https://github.com/nst/JSONTestSuite. +func TestJSONTestSuite(t *testing.T) { + entries, err := os.ReadDir("testdata/test_parsing") + if err != nil { + t.Fatal(err) + } + + for _, ent := range entries { + if !strings.HasSuffix(ent.Name(), ".json") { + continue + } + + t.Run(ent.Name(), func(t *testing.T) { + if skipTests[ent.Name()] { + t.Skip() + } + + data, err := os.ReadFile(filepath.Join("testdata/test_parsing", ent.Name())) + if err != nil { + t.Fatal(err) + } + + ours := testParse(data) + switch { + case strings.HasPrefix(ent.Name(), "y_"): // should accept + if ours != nil { + t.Errorf("must accept, got error: %v", ours) + } + case strings.HasPrefix(ent.Name(), "n_"): // should reject + if ours == nil { + t.Error("must reject") + } + case strings.HasPrefix(ent.Name(), "i_"): // implementation-specific, ensure we match stdlib + var v any + theirs := json.NewDecoder(bytes.NewReader(data)).Decode(&v) + if (ours == nil) != (theirs == nil) { + t.Errorf("stdlib mismatch: us=%v stdlib=%v", errstr(ours), errstr(theirs)) + } + } + }) + } +} + +func errstr(err error) string { + if err == nil { + return "accept" + } + return "reject: " + err.Error() +} diff --git a/aws-protocols/internal/json/parser.go b/aws-protocols/internal/json/parser.go new file mode 100644 index 000000000..4ecc01f27 --- /dev/null +++ b/aws-protocols/internal/json/parser.go @@ -0,0 +1,242 @@ +package json + +import ( + "errors" + "fmt" + "io" + "strconv" + + "github.com/aws/smithy-go/internal/serde" +) + +// matches stdlib +const maxDepth = 10_000 + +const ( + inObject int8 = iota + 1 + inArray +) + +func errUnexpectedToken(c byte) error { + return fmt.Errorf("unexpected token '%c'", c) +} + +// parser wraps scanner to pull tokens from a JSON body but also validate +// that the syntax of the JSON is valid. +type parser struct { + tok scanner + stack serde.Stack[int8] + done bool + + // parse changes as tokens are called to handle state transitions + // + // per the Dave Cheney article, https://www.json.org/json-en.html has flow + // diagrams that help us build out the state transitions + parse func(*parser, []byte) ([]byte, error) +} + +func (p *parser) Next() ([]byte, error) { + if p.done { + return nil, io.EOF + } + + next, err := p.tok.Next() + if err != nil { + if err == io.EOF { + return nil, fmt.Errorf("unexpected end of JSON input") + } + return nil, err + } + + return p.parse(p, next) +} + +func (p *parser) Skip() error { + var depth int + for { + tok, err := p.Next() + if err != nil { + return err + } + switch tok[0] { + case '{', '[': + depth++ + case '}', ']': + depth-- + } + if depth == 0 { + return nil + } + } +} + +func (p *parser) Value() (any, error) { + tok, err := p.Next() + if err != nil { + return nil, err + } + return p.value(tok) +} + +func (p *parser) parseValue(tok []byte) ([]byte, error) { + switch tok[0] { + case '{': + p.parse = (*parser).parseObjectKey + p.stack.Push(inObject) + if p.stack.Len() > maxDepth { + return nil, errors.New("exceeded max nesting depth") + } + return tok, nil + case '[': + p.parse = (*parser).parseArrayValue + p.stack.Push(inArray) + if p.stack.Len() > maxDepth { + return nil, errors.New("exceeded max nesting depth") + } + return tok, nil + case ',', ':', '}', ']': + return nil, errUnexpectedToken(tok[0]) + } + + p.afterValue() + return tok, nil +} + +func (p *parser) parseObjectKey(tok []byte) ([]byte, error) { + switch tok[0] { + case '"': + p.parse = (*parser).parseObjectColon + return tok, nil + case '}': + p.close() + return tok, nil + default: + return nil, errUnexpectedToken(tok[0]) + } +} + +func (p *parser) parseObjectColon(tok []byte) ([]byte, error) { + if tok[0] != ':' { + return nil, errUnexpectedToken(tok[0]) + } + p.parse = (*parser).parseValue + return p.Next() +} + +func (p *parser) parseObjectComma(tok []byte) ([]byte, error) { + switch tok[0] { + case ',': + p.parse = (*parser).parseObjectKeyAfterComma + return p.Next() + case '}': + p.close() + return tok, nil + default: + return nil, errUnexpectedToken(tok[0]) + } +} + +func (p *parser) parseObjectKeyAfterComma(tok []byte) ([]byte, error) { + if tok[0] != '"' { + return nil, errUnexpectedToken(tok[0]) + } + p.parse = (*parser).parseObjectColon + return tok, nil +} + +func (p *parser) parseArrayValue(tok []byte) ([]byte, error) { + if tok[0] == ']' { + p.close() + return tok, nil + } + return p.parseValue(tok) +} + +func (p *parser) parseArrayComma(tok []byte) ([]byte, error) { + switch tok[0] { + case ',': + p.parse = (*parser).parseValue + return p.Next() + case ']': + p.close() + return tok, nil + default: + return nil, errUnexpectedToken(tok[0]) + } +} + +func (p *parser) value(tok []byte) (any, error) { + switch tok[0] { + case 'n': + return nil, nil + case 't': + return true, nil + case 'f': + return false, nil + case '"': + return unquote(tok) + case '{': + m := map[string]any{} + for { + ktok, err := p.Next() + if err != nil { + return nil, err + } + if ktok[0] == '}' { + return m, nil + } + key, err := unquote(ktok) + if err != nil { + return nil, err + } + val, err := p.Value() + if err != nil { + return nil, err + } + m[key] = val + } + case '[': + var list []any + for { + tok, err := p.Next() + if err != nil { + return nil, err + } + if tok[0] == ']' { + if list == nil { + list = []any{} + } + return list, nil + } + val, err := p.value(tok) + if err != nil { + return nil, err + } + list = append(list, val) + } + default: + return strconv.ParseFloat(string(tok), 64) + } +} + +func (p *parser) close() { + p.stack.Pop() + p.afterValue() +} + +func (p *parser) afterValue() { + top := p.stack.Top() + if top == nil { + p.done = true + return + } + + switch *top { + case inObject: + p.parse = (*parser).parseObjectComma + case inArray: + p.parse = (*parser).parseArrayComma + default: + p.done = true + } +} diff --git a/aws-protocols/internal/json/scanner.go b/aws-protocols/internal/json/scanner.go new file mode 100644 index 000000000..9962cb27d --- /dev/null +++ b/aws-protocols/internal/json/scanner.go @@ -0,0 +1,191 @@ +package json + +import ( + "fmt" + "io" +) + +// scanner scans json tokens without the allocation overhead of json.Token. +// It does not care about the syntactic validity of the json at all, that is +// handled by [parser]. +// +// Largely inspired by https://dave.cheney.net/paste/gophercon-sg-2023.html +// although this version operates on a fully buffered input since we are +// generally dealing with smaller RPC-style payloads. +type scanner struct { + p []byte + i int +} + +func (s *scanner) Next() ([]byte, error) { + i := s.i + for ; i < len(s.p); i++ { + c := s.p[i] + if whitespace[c] { + continue + } + if delim[c] { + i++ + s.i = i + return s.p[i-1 : i], nil + } + + s.i = i + switch c { + case '"': + return s.scanString() + case 't': + return s.scanLiteral("true") + case 'f': + return s.scanLiteral("false") + case 'n': + return s.scanLiteral("null") + default: + return s.scanNumber() + } + } + + s.i = i + return nil, io.EOF +} + +func (s *scanner) IsEOF() bool { + return s.i >= len(s.p) +} + +func (s *scanner) scanString() ([]byte, error) { + start := s.i + i := s.i + 1 // skip opening " + + for i < len(s.p) { + c := s.p[i] + if c < 0x20 { + s.i = i + return nil, fmt.Errorf("invalid control character at offset %d", i) + } + + if c == '\\' { + s.i = i + 1 + if err := s.scanEscape(); err != nil { + return nil, err + } + i = s.i + continue + } + + if c == '"' { + i++ + s.i = i + + // we want the quotes here because this lets the token consumer + // know that it's a string without additional identifying data + // (e.g. a token type enum) + return s.p[start:i], nil + } + + i++ + } + s.i = i + return nil, fmt.Errorf("unterminated string at offset %d", start) +} + +func (s *scanner) scanEscape() error { + if s.i >= len(s.p) { + return fmt.Errorf("unterminated escape at offset %d", s.i-1) + } + c := s.p[s.i] + s.i++ + switch c { + case '"', '\\', '/', 'b', 'f', 'n', 'r', 't': + return nil + case 'u': + return s.scanUnicodeEscape() + default: + return fmt.Errorf("invalid escape character '%c' at offset %d", c, s.i-1) + } +} + +func (s *scanner) scanUnicodeEscape() error { + if s.i+4 > len(s.p) { + return fmt.Errorf("incomplete unicode escape at offset %d", s.i-2) + } + for _, c := range s.p[s.i : s.i+4] { + if !('0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F') { + return fmt.Errorf("invalid character '%c' in unicode escape at offset %d", c, s.i-2) + } + } + s.i += 4 + return nil +} + +func (s *scanner) scanNumber() ([]byte, error) { + i := s.i + if i < len(s.p) && s.p[i] == '-' { + i++ + } + digitStart := i + for i < len(s.p) && s.p[i] >= '0' && s.p[i] <= '9' { + i++ + } + if i == digitStart { + return nil, fmt.Errorf("unexpected token at offset %d", s.i) + } + if s.p[digitStart] == '0' && i-digitStart > 1 { + return nil, fmt.Errorf("leading zeros not allowed at offset %d", digitStart) + } + + if i < len(s.p) && s.p[i] == '.' { + i++ + digitStart = i + for i < len(s.p) && s.p[i] >= '0' && s.p[i] <= '9' { + i++ + } + if i == digitStart { + return nil, fmt.Errorf("no digits after decimal point at offset %d", i) + } + } + + if i < len(s.p) && (s.p[i] == 'e' || s.p[i] == 'E') { + i++ + if i < len(s.p) && (s.p[i] == '+' || s.p[i] == '-') { + i++ + } + digitStart = i + for i < len(s.p) && s.p[i] >= '0' && s.p[i] <= '9' { + i++ + } + if i == digitStart { + return nil, fmt.Errorf("no digits after exponent at offset %d", i) + } + } + + start := s.i + s.i = i + return s.p[start:i], nil +} + +func (s *scanner) scanLiteral(lit string) ([]byte, error) { + end := s.i + len(lit) + if end > len(s.p) || string(s.p[s.i:end]) != lit { + return nil, fmt.Errorf("invalid literal at offset %d", s.i) + } + start := s.i + s.i = end + return s.p[start:end], nil +} + +var whitespace = [256]bool{ + ' ': true, + '\t': true, + '\n': true, + '\r': true, +} + +var delim = [256]bool{ + '{': true, + '}': true, + '[': true, + ']': true, + ':': true, + ',': true, +} diff --git a/aws-protocols/internal/json/scanner_test.go b/aws-protocols/internal/json/scanner_test.go new file mode 100644 index 000000000..af31326c1 --- /dev/null +++ b/aws-protocols/internal/json/scanner_test.go @@ -0,0 +1,557 @@ +// Tests in this file are derived from the Go standard library's encoding/json +// to validate matching behavior. +// +// see https://cs.opensource.google/go/go/+/refs/tags/go1.24.0:src/encoding/json/ + +package json + +import ( + "io" + "math" + "reflect" + "strings" + "testing" +) + +func testParse(p []byte) error { + pr := parser{ + tok: scanner{p: p}, + parse: (*parser).parseValue, + } + for { + _, err := pr.Next() + if err == io.EOF { + return nil + } + if err != nil { + return err + } + } +} + +// --------------------------------------------------------------------------- +// From: src/encoding/json/scanner_test.go — TestValid +// +// Tests basic JSON validity detection. +// --------------------------------------------------------------------------- + +func TestValid(t *testing.T) { + // Cases from the stdlib TestValid table. + tests := []struct { + data string + ok bool + }{ + {`foo`, false}, + {`}{`, false}, + {`{]`, false}, + {`{}`, true}, + {`{"foo":"bar"}`, true}, + {`{"foo":"bar","bar":{"baz":["qux"]}}`, true}, + } + + // Additional cases exercising our scanner. + additional := []struct { + data string + ok bool + }{ + {`[]`, true}, + {`[1,2,3]`, true}, + {`null`, true}, + {`true`, true}, + {`false`, true}, + {`"hello"`, true}, + {`42`, true}, + {`-1.5e10`, true}, + } + tests = append(tests, additional...) + + for _, tt := range tests { + err := testParse([]byte(tt.data)) + got := err == nil + if got != tt.ok { + t.Errorf("drain(%q) valid=%v, want %v (err=%v)", tt.data, got, tt.ok, err) + } + } +} + +// --------------------------------------------------------------------------- +// From: src/encoding/json/decode_test.go — TestUnmarshalSyntax +// +// Tests that syntactically invalid JSON produces errors. +// --------------------------------------------------------------------------- + +func TestSyntaxErrors(t *testing.T) { + // Cases from the stdlib TestUnmarshalSyntax table. + stdlibCases := []string{ + "tru", + "fals", + "nul", + `"hello`, + `[1,2,3`, + `{"key":1`, + `{"key":1,`, + } + + // Additional cases from stdlib unmarshalTests (syntax error entries). + fromUnmarshalTests := []string{ + `{"X": "foo", "Y"}`, // missing colon + `[1, 2, 3+]`, // invalid char after element + `[2, 3`, // unexpected EOF + } + + // Additional cases exercising our parser's state machine. + additional := []string{ + `{]`, + `[}`, + `{"a" "b"}`, // missing colon + `[1 2]`, // missing comma + `{"a":}`, // missing value + `{:1}`, // missing key + `[,]`, // leading comma + `{,}`, // leading comma + `[1,,2]`, // double comma + } + + all := append(stdlibCases, fromUnmarshalTests...) + all = append(all, additional...) + + for _, tt := range all { + if err := testParse([]byte(tt)); err == nil { + t.Errorf("drain(%q) = nil, want error", tt) + } + } +} + +// --------------------------------------------------------------------------- +// From: src/encoding/json/stream_test.go — TestDecodeInStream +// +// Tests that the parser produces the correct token sequence. The stdlib test +// uses json.Token (interface values); we compare raw byte tokens instead since +// our parser returns []byte slices. +// --------------------------------------------------------------------------- + +func TestTokenStream(t *testing.T) { + // Cases from the stdlib TestDecodeInStream table (token-only entries, + // excluding decodeThis cases which test Decode-into-value). + tests := []struct { + json string + tokens []string + }{ + {`10`, []string{`10`}}, + {` [10] `, []string{`[`, `10`, `]`}}, + {` [false,10,"b"] `, []string{`[`, `false`, `10`, `"b"`, `]`}}, + {`{ "a": 1 }`, []string{`{`, `"a"`, `1`, `}`}}, + {`{"a": 1, "b":"3"}`, []string{`{`, `"a"`, `1`, `"b"`, `"3"`, `}`}}, + {` [{"a": 1},{"a": 2}] `, []string{ + `[`, `{`, `"a"`, `1`, `}`, `{`, `"a"`, `2`, `}`, `]`, + }}, + {`{"obj": {"a": 1}}`, []string{ + `{`, `"obj"`, `{`, `"a"`, `1`, `}`, `}`, + }}, + {`{"obj": [{"a": 1}]}`, []string{ + `{`, `"obj"`, `[`, `{`, `"a"`, `1`, `}`, `]`, `}`, + }}, + } + + // Additional cases. + additional := []struct { + json string + tokens []string + }{ + {`null`, []string{`null`}}, + {`true`, []string{`true`}}, + {`false`, []string{`false`}}, + {`"hello"`, []string{`"hello"`}}, + {`""`, []string{`""`}}, + {`[null, true, false]`, []string{`[`, `null`, `true`, `false`, `]`}}, + } + tests = append(tests, additional...) + + for _, tt := range tests { + t.Run(tt.json, func(t *testing.T) { + p := parser{ + tok: scanner{p: []byte(tt.json)}, + parse: (*parser).parseValue, + } + var got []string + for { + tok, err := p.Next() + if err == io.EOF { + break + } + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + got = append(got, string(tok)) + } + if !reflect.DeepEqual(got, tt.tokens) { + t.Errorf("tokens mismatch:\n got: %v\n want: %v", got, tt.tokens) + } + }) + } +} + +// --------------------------------------------------------------------------- +// From: src/encoding/json/decode_test.go — unmarshalTests +// +// Tests that parser.Value() decodes JSON into Go values matching the behavior +// of encoding/json.Decoder.Decode into any (numbers become float64, objects +// become map[string]any, arrays become []any). +// --------------------------------------------------------------------------- + +func TestValue(t *testing.T) { + // Cases drawn from the stdlib unmarshalTests table (adapted for untyped + // decoding into any). + tests := []struct { + in string + out any + }{ + // Basic types — from unmarshalTests lines for bool, int, float, string. + {`true`, true}, + {`false`, false}, + {`null`, nil}, + {`1`, float64(1)}, + {`1.2`, float64(1.2)}, + {`-5`, float64(-5)}, + {`2`, float64(2)}, + {`0`, float64(0)}, + {`-0`, float64(0)}, + {`1e2`, float64(100)}, + {`1.5e1`, float64(15)}, + {`-5e+2`, float64(-500)}, + {`3e-3`, float64(0.003)}, + + // String escapes — from unmarshalTests. + {`"a\u1234"`, "a\u1234"}, + {`"http:\/\/"`, "http://"}, + {`"g-clef: \uD834\uDD1E"`, "g-clef: \U0001D11E"}, + {`"invalid: \uD834x\uDD1E"`, "invalid: \uFFFDx\uFFFD"}, + + // Whitespace — from unmarshalTests "raw values with whitespace" section. + {"\n true ", true}, + {"\t 1 ", float64(1)}, + {"\r 1.2 ", float64(1.2)}, + {"\t -5 \n", float64(-5)}, + {"\t \"a\\u1234\" \n", "a\u1234"}, + + // Complex nested — from unmarshalTests ifaceNumAsFloat64 case. + {`{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, + map[string]any{ + "k1": float64(1), + "k2": "s", + "k3": []any{float64(1), float64(2.0), float64(0.003)}, + "k4": map[string]any{"kk1": "s", "kk2": float64(2)}, + }}, + } + + // Additional cases. + additional := []struct { + in string + out any + }{ + {`"hello"`, "hello"}, + {`""`, ""}, + {`"tab:\t"`, "tab:\t"}, + {`"newline:\n"`, "newline:\n"}, + {`"quote:\""`, "quote:\""}, + {`"backslash:\\"`, "backslash:\\"}, + {`"slash:\/"`, "slash:/"}, + {`"cr:\r"`, "cr:\r"}, + {`"formfeed:\f"`, "formfeed:\f"}, + {`"backspace:\b"`, "backspace:\b"}, + {`[]`, []any{}}, + {`[1,2,3]`, []any{float64(1), float64(2), float64(3)}}, + {`[true, false, null]`, []any{true, false, nil}}, + {`["a", "b"]`, []any{"a", "b"}}, + {`[[1],[2]]`, []any{[]any{float64(1)}, []any{float64(2)}}}, + {`{}`, map[string]any{}}, + {`{"a":1}`, map[string]any{"a": float64(1)}}, + {`{"a":1,"b":"two"}`, map[string]any{"a": float64(1), "b": "two"}}, + {`{"nested":{"x":true}}`, map[string]any{"nested": map[string]any{"x": true}}}, + } + tests = append(tests, additional...) + + for _, tt := range tests { + t.Run(tt.in, func(t *testing.T) { + p := parser{ + tok: scanner{p: []byte(tt.in)}, + parse: (*parser).parseValue, + } + got, err := p.Value() + if err != nil { + t.Fatalf("Value(%q) error: %v", tt.in, err) + } + if !reflect.DeepEqual(got, tt.out) { + t.Errorf("Value(%q):\n got: %#v\n want: %#v", tt.in, got, tt.out) + } + }) + } +} + +// --------------------------------------------------------------------------- +// From: src/encoding/json/number_test.go — TestNumberIsValid +// +// Tests valid and invalid JSON number formats. +// --------------------------------------------------------------------------- + +func TestNumberParsing(t *testing.T) { + // Valid numbers from the stdlib TestNumberIsValid table. + valid := []string{ + "0", + "-0", + "1", + "-1", + "0.1", + "-0.1", + "1234", + "-1234", + "12.34", + "-12.34", + "12E0", + "12E1", + "12e34", + "12E-0", + "12e+1", + "12e-34", + "-12E0", + "-12E1", + "-12e34", + "-12E-0", + "-12e+1", + "-12e-34", + "1.2E0", + "1.2E1", + "1.2e34", + "1.2E-0", + "1.2e+1", + "1.2e-34", + "-1.2E0", + "-1.2E1", + "-1.2e34", + "-1.2E-0", + "-1.2e+1", + "-1.2e-34", + "0E0", + "0E1", + "0e34", + "0E-0", + "0e+1", + "0e-34", + "-0E0", + "-0E1", + "-0e34", + "-0E-0", + "-0e+1", + "-0e-34", + } + for _, tt := range valid { + if err := testParse([]byte(tt)); err != nil { + t.Errorf("drain(%q) = %v, want nil", tt, err) + } + } + + // Invalid numbers from the stdlib TestNumberIsValid table. + // Note: some stdlib cases (like "123e", "1e", "1e+", "01", "012") test + // isValidNumber() which rejects them statically. Our scanner may accept + // the token but the parser's state machine or strconv.ParseFloat will + // reject them downstream. We only include cases our scanner itself + // should reject. + invalid := []string{ + "", + "invalid", + "1..1", + "1e+-2", + "1e--23", + "e1", + "1ea", + "1.a", + } + for _, tt := range invalid { + if err := testParse([]byte(tt)); err == nil { + t.Errorf("drain(%q) = nil, want error", tt) + } + } +} + +// --------------------------------------------------------------------------- +// From: src/encoding/json/decode_test.go — unmarshalTests (string entries) +// +// Tests string escape sequences including JSON-specific escapes (\/) and +// UTF-16 surrogate pairs (\uD800\uDC00) that differ from Go string literals. +// --------------------------------------------------------------------------- + +func TestStringEscapes(t *testing.T) { + // Cases from stdlib unmarshalTests and the unquoteBytes implementation. + tests := []struct { + in string + want string + }{ + {`"\""`, `"`}, + {`"\\"`, `\`}, + {`"\/"`, `/`}, // JSON-specific: \/ is valid + {`"\b"`, "\b"}, + {`"\f"`, "\f"}, + {`"\n"`, "\n"}, + {`"\r"`, "\r"}, + {`"\t"`, "\t"}, + {`"\u0041"`, "A"}, + {`"\u00e9"`, "é"}, + {`"\u0000"`, "\x00"}, + {`"\uD800\uDC00"`, "\U00010000"}, // surrogate pair: U+10000 + {`"\uD834\uDD1E"`, "\U0001D11E"}, // surrogate pair: G clef + {`"no escape"`, "no escape"}, + {`""`, ""}, + } + for _, tt := range tests { + t.Run(tt.in, func(t *testing.T) { + tok := scanner{p: []byte(tt.in)} + raw, err := tok.Next() + if err != nil { + t.Fatalf("tokenize error: %v", err) + } + got, err := unquote(raw) + if err != nil { + t.Fatalf("str error: %v", err) + } + if got != tt.want { + t.Errorf("unquote(%s) = %q, want %q", tt.in, got, tt.want) + } + }) + } +} + +// --------------------------------------------------------------------------- +// Additional tests not directly from stdlib but validating equivalent behavior. +// --------------------------------------------------------------------------- + +func TestInvalidStrings(t *testing.T) { + tests := []string{ + `"`, // unterminated + `"hello`, // unterminated + "\"hello\n\"", // unescaped newline (invalid per RFC 8259 §7) + "\"hello\r\"", // unescaped carriage return + } + for _, tt := range tests { + tok := scanner{p: []byte(tt)} + _, err := tok.Next() + if err == nil { + t.Errorf("tokenize(%q) = nil, want error", tt) + } + } +} + +func TestFloatSpecialValues(t *testing.T) { + tests := []struct { + in string + out float64 + }{ + {"0", 0}, + {"-0", math.Copysign(0, -1)}, + {"1e308", 1e308}, + {"-1e308", -1e308}, + {"5e-324", 5e-324}, + {"1.7976931348623157e308", math.MaxFloat64}, + } + for _, tt := range tests { + t.Run(tt.in, func(t *testing.T) { + p := parser{ + tok: scanner{p: []byte(tt.in)}, + parse: (*parser).parseValue, + } + v, err := p.Value() + if err != nil { + t.Fatalf("Value(%q) error: %v", tt.in, err) + } + got, ok := v.(float64) + if !ok { + t.Fatalf("Value(%q) = %T, want float64", tt.in, v) + } + if got != tt.out { + t.Errorf("Value(%q) = %v, want %v", tt.in, got, tt.out) + } + }) + } +} + +func TestSkip(t *testing.T) { + tests := []struct { + json string + after string + }{ + {`[1,2,3], "after"`, "after"}, + {`{"a":1}, "after"`, "after"}, + {`"str", "after"`, "after"}, + {`123, "after"`, "after"}, + {`true, "after"`, "after"}, + {`null, "after"`, "after"}, + {`{"a":{"b":[1,2,{"c":3}]}}, "after"`, "after"}, + } + for _, tt := range tests { + t.Run(tt.json, func(t *testing.T) { + wrapped := `[` + tt.json + `]` + p := parser{ + tok: scanner{p: []byte(wrapped)}, + parse: (*parser).parseValue, + } + tok, err := p.Next() + if err != nil || string(tok) != "[" { + t.Fatalf("expected '[', got %q err=%v", tok, err) + } + if err := p.Skip(); err != nil { + t.Fatalf("Skip() error: %v", err) + } + tok, err = p.Next() + if err != nil { + t.Fatalf("Next() after Skip error: %v", err) + } + got, _ := unquote(tok) + if got != tt.after { + t.Errorf("after Skip: got %q, want %q", got, tt.after) + } + }) + } +} + +func TestLargeInput(t *testing.T) { + // Deeply nested object. + var b strings.Builder + depth := 100 + for i := 0; i < depth; i++ { + b.WriteString(`{"a":`) + } + b.WriteString(`1`) + for i := 0; i < depth; i++ { + b.WriteString(`}`) + } + if err := testParse([]byte(b.String())); err != nil { + t.Fatalf("drain(nested %d deep) error: %v", depth, err) + } + + // Large array. + b.Reset() + b.WriteString(`[`) + for i := 0; i < 10000; i++ { + if i > 0 { + b.WriteString(`,`) + } + b.WriteString(`{"key":"value","num":123}`) + } + b.WriteString(`]`) + if err := testParse([]byte(b.String())); err != nil { + t.Fatalf("drain(large array) error: %v", err) + } +} + +func TestWhitespace(t *testing.T) { + tests := []string{ + " \t\r\n{} ", + " [ 1 , 2 , 3 ] ", + "\n\n{\n\"a\"\n:\n1\n}\n", + "\t{\t\"key\"\t:\t\"value\"\t}\t", + } + for _, tt := range tests { + if err := testParse([]byte(tt)); err != nil { + t.Errorf("drain(%q) = %v, want nil", tt, err) + } + } +} diff --git a/aws-protocols/internal/json/shape_deserializer.go b/aws-protocols/internal/json/shape_deserializer.go index ea7c0a050..b008041e6 100644 --- a/aws-protocols/internal/json/shape_deserializer.go +++ b/aws-protocols/internal/json/shape_deserializer.go @@ -1,30 +1,43 @@ package json import ( - "bytes" "encoding/base64" - "encoding/json" "fmt" "math" + "strconv" "strings" "time" "github.com/aws/smithy-go" + "github.com/aws/smithy-go/aws-protocols/internal/json/internal/stdlib" "github.com/aws/smithy-go/document" + "github.com/aws/smithy-go/internal/serde" smithytime "github.com/aws/smithy-go/time" "github.com/aws/smithy-go/traits" ) +type ctxKind int8 + +const ( + ctxList ctxKind = iota + 1 + ctxMap + ctxStruct + ctxUnion +) + +type deserCtx struct { + kind ctxKind + schema *smithy.Schema // for ctxStruct +} + // ShapeDeserializer implements unmarshaling of JSON into Smithy shapes. type ShapeDeserializer struct { - dec *json.Decoder - head jsonStack + p parser + head serde.Stack[deserCtx] opts Options - // json.Decoder does not have a Peek() but we need to be able to - // "lookahead" for conditionally pulling a null token out in ReadNil. - peeked json.Token - hasPeek bool + // it's easier to just maintain the "peeked" token here actually + peeked []byte } // NewShapeDeserializer creates a new ShapeDeserializer. @@ -33,58 +46,49 @@ func NewShapeDeserializer(p []byte, opts ...func(*Options)) *ShapeDeserializer { for _, fn := range opts { fn(&o) } - dec := json.NewDecoder(bytes.NewReader(p)) - dec.UseNumber() - return &ShapeDeserializer{dec: dec, head: newJSONStack(), opts: o} + return &ShapeDeserializer{ + p: parser{ + tok: scanner{p: p}, + parse: (*parser).parseValue, + }, + head: serde.NewStack[deserCtx](), + opts: o, + } } var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil) -func (d *ShapeDeserializer) token() (json.Token, error) { - if d.hasPeek { - d.hasPeek = false - return d.peeked, nil +func (d *ShapeDeserializer) next() ([]byte, error) { + if d.peeked != nil { + peeked := d.peeked + d.peeked = nil + return peeked, nil } - return d.dec.Token() + return d.p.Next() } -func (d *ShapeDeserializer) more() bool { - if d.hasPeek { - return true +func (d *ShapeDeserializer) peek() ([]byte, error) { + if d.peeked != nil { + return d.peeked, nil } - return d.dec.More() -} - -func (d *ShapeDeserializer) expectDelim(e json.Delim) error { - tok, err := d.token() + tok, err := d.p.Next() if err != nil { - return err - } - - if a, ok := tok.(json.Delim); ok { - if e != a { - return fmt.Errorf("expect %s, got %s", e, a) - } - return nil + return nil, err } - - return fmt.Errorf("expect delim, got %T", tok) + d.peeked = tok + return tok, nil } // ReadNil implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadNil(s *smithy.Schema) (bool, error) { - tok, err := d.token() + tok, err := d.peek() if err != nil { return false, err } - if tok == nil { + if isN(tok) { + d.peeked = nil return true, nil } - - // The only way to "unread" it is to note it and have token() return it - // next time. - d.peeked = tok - d.hasPeek = true return false, nil } @@ -137,17 +141,16 @@ func (d *ShapeDeserializer) ReadInt64Ptr(s *smithy.Schema, v **int64) error { } func (d *ShapeDeserializer) readInt(min, max int64) (int64, error) { - tok, err := d.token() + tok, err := d.next() if err != nil { return 0, err } - num, ok := tok.(json.Number) - if !ok { - return 0, fmt.Errorf("expected number, got %T", tok) + if isS(tok) || isLCB(tok) || isLSB(tok) { + return 0, fmt.Errorf("expected number, got %s", tok) } - n, err := num.Int64() + n, err := strconv.ParseInt(string(tok), 10, 64) if err != nil { return 0, err } @@ -184,44 +187,48 @@ func (d *ShapeDeserializer) ReadFloat64Ptr(s *smithy.Schema, v **float64) error } func (d *ShapeDeserializer) readFloat() (float64, error) { - tok, err := d.token() + tok, err := d.next() if err != nil { return 0, err } - switch v := tok.(type) { - case json.Number: - return v.Float64() - case string: + if isS(tok) { + s, err := unquote(tok) + if err != nil { + return 0, err + } switch { - case strings.EqualFold(v, "NaN"): + case strings.EqualFold(s, "NaN"): return math.NaN(), nil - case strings.EqualFold(v, "Infinity"): + case strings.EqualFold(s, "Infinity"): return math.Inf(1), nil - case strings.EqualFold(v, "-Infinity"): + case strings.EqualFold(s, "-Infinity"): return math.Inf(-1), nil default: - return 0, fmt.Errorf("unexpected string value for float: %s", v) + return 0, fmt.Errorf("unexpected string value for float: %s", s) } - default: - return 0, fmt.Errorf("expected number, got %T", tok) } + + return strconv.ParseFloat(string(tok), 64) } // ReadBool implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { - tok, err := d.token() + tok, err := d.next() if err != nil { return err } - b, ok := tok.(bool) - if !ok { - return fmt.Errorf("expected bool, got %T", tok) + switch { + case isT(tok): + *v = true + return nil + case isF(tok): + *v = false + return nil + default: + return fmt.Errorf("expected bool, got %s", tok) } - - *v = b - return nil } // ReadBoolPtr implements [smithy.ShapeDeserializer]. @@ -231,7 +238,7 @@ func (d *ShapeDeserializer) ReadBoolPtr(s *smithy.Schema, v **bool) error { // ReadString implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { - tok, err := d.token() + tok, err := d.next() if err != nil { return err } @@ -239,12 +246,16 @@ func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { return nil } - str, ok := tok.(string) - if !ok { - return fmt.Errorf("expected string, got %T", tok) + if !isS(tok) { + return fmt.Errorf("expected string, got %s", tok) + } + + sv, err := unquote(tok) + if err != nil { + return err } - *v = str + *v = sv return nil } @@ -306,17 +317,21 @@ func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { return err } - tok, err := d.token() + tok, err := d.next() if err != nil { return err } - str, ok := tok.(string) - if !ok { - return fmt.Errorf("expected string, got %T", tok) + if !isS(tok) { + return fmt.Errorf("expected string, got %s", tok) } - b, err := base64.StdEncoding.DecodeString(str) + sv, err := unquote(tok) + if err != nil { + return err + } + + b, err := base64.StdEncoding.DecodeString(sv) if err != nil { return fmt.Errorf("decode base64 blob: %w", err) } @@ -327,59 +342,59 @@ func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { // ReadList implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { - tok, err := d.token() + tok, err := d.next() if err != nil { return err } - - delim, ok := tok.(json.Delim) - if !ok || delim != '[' { - return fmt.Errorf("expected '[', got %v", tok) + if !isLSB(tok) { + return fmt.Errorf("expected '[', got %s", tok) } - + d.head.Push(deserCtx{kind: ctxList}) return nil } // ReadListItem implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadListItem(s *smithy.Schema) (bool, error) { - if !d.more() { - return false, d.expectDelim(']') + tok, err := d.peek() + if err != nil { + return false, err + } + if isRSB(tok) { + d.peeked = nil + d.head.Pop() + return false, nil } - return true, nil } // ReadMap implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadMap(s *smithy.Schema) error { - tok, err := d.token() + tok, err := d.next() if err != nil { return err } - - delim, ok := tok.(json.Delim) - if !ok || delim != '{' { - return fmt.Errorf("expected '{', got %v", tok) + if !isLCB(tok) { + return fmt.Errorf("expected '{', got %s", tok) } - + d.head.Push(deserCtx{kind: ctxMap}) return nil } // ReadMapKey implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadMapKey(s *smithy.Schema) (string, bool, error) { - if !d.more() { - return "", false, d.expectDelim('}') - } - - tok, err := d.token() + tok, err := d.next() if err != nil { return "", false, err } - - key, ok := tok.(string) - if !ok { - return "", false, fmt.Errorf("expected string key, got %T", tok) + if isRCB(tok) { + d.head.Pop() + return "", false, nil } + key, err := unquote(tok) + if err != nil { + return "", false, err + } return key, true, nil } @@ -389,45 +404,44 @@ func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error { return err } - tok, err := d.token() + tok, err := d.next() if err != nil { return err } - - delim, ok := tok.(json.Delim) - if !ok || delim != '{' { - return fmt.Errorf("expected '{', got %v", tok) + if !isLCB(tok) { + return fmt.Errorf("expected '{', got %s", tok) } - - d.head.Push(s) + d.head.Push(deserCtx{kind: ctxStruct, schema: s}) return nil } // ReadStructMember implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { - if !d.more() { - d.head.Pop() - return nil, d.expectDelim('}') - } - - tok, err := d.token() + tok, err := d.next() if err != nil { return nil, err } - - key, ok := tok.(string) - if !ok { - return nil, fmt.Errorf("expected string key, got %T", tok) + if isRCB(tok) { + d.head.Pop() + return nil, nil } - schema, ok := d.head.Top().(*smithy.Schema) - if !ok { + top := d.head.Top() + if top == nil || top.kind != ctxStruct { return nil, fmt.Errorf("ReadStructMember called without ReadStruct?") } - member := schema.Member(key) + member, err := memberFromToken(top.schema, tok) + if err != nil { + return nil, err + } + if member == nil && d.opts.UseJSONName { - for _, m := range schema.Members() { + key, err := unquote(tok) + if err != nil { + return nil, err + } + for _, m := range top.schema.Members() { if jn, ok := smithy.SchemaTrait[*traits.JSONName](m); ok && jn.Name == key { member = m break @@ -435,45 +449,40 @@ func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { } } if member == nil { - if err := d.skip(); err != nil { + if err := d.p.Skip(); err != nil { return nil, err } - return d.ReadStructMember() // just try the next one + return d.ReadStructMember() } return member, nil } -type unionCtx struct { - schema *smithy.Schema -} - // ReadUnion implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) { - if _, ok := d.head.Top().(*unionCtx); !ok { + if top := d.head.Top(); top == nil || top.kind != ctxUnion { if isNil, err := d.ReadNil(s); isNil || err != nil { return nil, err } - tok, err := d.token() + tok, err := d.next() if err != nil { return nil, err } - delim, ok := tok.(json.Delim) - if !ok || delim != '{' { - return nil, fmt.Errorf("expected '{', got %v", tok) + if !isLCB(tok) { + return nil, fmt.Errorf("expected '{', got %s", tok) } - d.head.Push(&unionCtx{schema: s}) + d.head.Push(deserCtx{kind: ctxUnion}) } - for d.more() { - tok, err := d.token() + for { + tok, err := d.next() if err != nil { return nil, err } - key, ok := tok.(string) - if !ok { - return nil, fmt.Errorf("expected string key, got %T", tok) + if isRCB(tok) { + d.head.Pop() + return nil, nil } // skip null values @@ -485,8 +494,16 @@ func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) continue } - member := s.Member(key) + member, err := memberFromToken(s, tok) + if err != nil { + return nil, err + } + if member == nil && d.opts.UseJSONName { + key, err := unquote(tok) + if err != nil { + return nil, err + } for _, m := range s.Members() { if jn, ok := smithy.SchemaTrait[*traits.JSONName](m); ok && jn.Name == key { member = m @@ -495,7 +512,7 @@ func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) } } if member == nil { - if err := d.skip(); err != nil { + if err := d.p.Skip(); err != nil { return nil, err } continue @@ -503,62 +520,15 @@ func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) return member, nil } - - d.head.Pop() - return nil, d.expectDelim('}') -} - -// used to skip over a struct member that we didn't have a schema for, though -// it also calls itself -func (d *ShapeDeserializer) skip() error { - tok, err := d.token() - if err != nil { - return err - } - - switch v := tok.(type) { - case json.Delim: - switch v { - case '{': - for d.more() { - if _, err := d.token(); err != nil { // the key - return err - } - if err := d.skip(); err != nil { // the value - return err - } - } - _, err := d.token() // the '}' - return err - case '[': - for d.more() { - if err := d.skip(); err != nil { - return err - } - } - _, err := d.token() // the ']' - return err - default: - return fmt.Errorf("unexpected delimiter: %v", v) - } - default: - return nil // scalar, don't have to do anything else - } } // ReadDocument reads a JSON value into a document Value. -// -// For now this produces an [document.Opaque] wrapping the raw decoded value, -// which is what the legacy document bridge in generated code expects. The -// jsonToValue conversion is available for future use when the new typed -// document path is wired up end-to-end. func (d *ShapeDeserializer) ReadDocument(schema *smithy.Schema, v *document.Value) error { - var raw any - if err := d.dec.Decode(&raw); err != nil { + vv, err := d.p.Value() + if err != nil { return err } - - *v = document.Opaque{Value: raw} + *v = document.Opaque{Value: vv} return nil } @@ -573,39 +543,35 @@ func readPtr[T any](d *ShapeDeserializer, s *smithy.Schema, v **T, read func(*sm return read(s, *v) } -func jsonToValue(v any) (document.Value, error) { - switch vv := v.(type) { - case nil: - return document.Null{}, nil - case bool: - return document.Boolean(vv), nil - case json.Number: - return document.Number(vv.String()), nil - case float64: - return document.Number(fmt.Sprintf("%v", vv)), nil - case string: - return document.String(vv), nil - case []any: - list := make(document.List, len(vv)) - for i, item := range vv { - dv, err := jsonToValue(item) - if err != nil { - return nil, err - } - list[i] = dv - } - return list, nil - case map[string]any: - m := make(document.Map, len(vv)) - for k, item := range vv { - dv, err := jsonToValue(item) - if err != nil { - return nil, err - } - m[k] = dv - } +func unquote(tok []byte) (string, error) { + if s, ok := stdlib.UnquoteBytes(tok); ok { + return string(s), nil + } + return "", fmt.Errorf("cannot unquote %s", tok) +} + +func memberFromToken(s *smithy.Schema, tok []byte) (*smithy.Schema, error) { + // the fast path should be basically everything since members will usually + // not have escaped chars, so we save an allocation by skipping unquote + inner := tok[1 : len(tok)-1] + if m := s.Member(string(inner)); m != nil { return m, nil - default: - return nil, fmt.Errorf("unexpected JSON type %T", v) } + + // otherwise unquote it like everything else + unq, err := unquote(tok) + if err != nil { + return nil, err + } + + return s.Member(unq), nil } + +func isN(tok []byte) bool { return tok[0] == 'n' } +func isT(tok []byte) bool { return tok[0] == 't' } +func isF(tok []byte) bool { return tok[0] == 'f' } +func isS(tok []byte) bool { return tok[0] == '"' } +func isLCB(tok []byte) bool { return tok[0] == '{' } +func isRCB(tok []byte) bool { return tok[0] == '}' } +func isLSB(tok []byte) bool { return tok[0] == '[' } +func isRSB(tok []byte) bool { return tok[0] == ']' } diff --git a/aws-protocols/internal/json/shape_serializer.go b/aws-protocols/internal/json/shape_serializer.go index 78681e526..33d178ce8 100644 --- a/aws-protocols/internal/json/shape_serializer.go +++ b/aws-protocols/internal/json/shape_serializer.go @@ -16,7 +16,7 @@ import ( // ShapeSerializer implements marshaling of Smithy shapes to JSON. type ShapeSerializer struct { root *smithyjson.Encoder - head jsonStack + head stackT[any] opts Options } @@ -43,7 +43,6 @@ func NewShapeSerializer(opts ...func(*Options)) *ShapeSerializer { } return &ShapeSerializer{ root: smithyjson.NewEncoder(), - head: newJSONStack(), opts: o, } } @@ -430,35 +429,6 @@ func (s *ShapeSerializer) writeDocumentRaw(schema *smithy.Schema, p []byte) { } } -type jsonStack struct { - values []any -} - -func newJSONStack() jsonStack { - return jsonStack{values: make([]any, 0, 8)} -} - -type empty struct{} - -func (s *jsonStack) Top() any { - if len(s.values) == 0 { - return empty{} - } - return s.values[len(s.values)-1] -} - -func (s *jsonStack) Push(v any) { - s.values = append(s.values, v) -} - -func (s *jsonStack) Pop() { - s.values = s.values[:len(s.values)-1] -} - -func (s *jsonStack) Len() int { - return len(s.values) -} - // jsonMemberName returns the JSON key for a schema member, using the // jsonName trait if UseJSONName is enabled, otherwise the member name. func (s *ShapeSerializer) jsonMemberName(schema *smithy.Schema) string { diff --git a/aws-protocols/internal/json/stack.go b/aws-protocols/internal/json/stack.go new file mode 100644 index 000000000..8ee85b6be --- /dev/null +++ b/aws-protocols/internal/json/stack.go @@ -0,0 +1,38 @@ +package json + +// chosen as an arbitrary average max depth for the RPC-style payloads we +// typically deal with in an SDK client +const initialCap = 8 + +type stackT[T any] struct { + values []T + sentinel T +} + +func newStackT[T any](sentinel T) stackT[T] { + return stackT[T]{ + values: make([]T, 0, initialCap), + sentinel: sentinel, + } +} + +func (s *stackT[T]) Push(v T) { + s.values = append(s.values, v) +} + +func (s *stackT[T]) Pop() T { + if len(s.values) == 0 { + return s.sentinel + } + idx := len(s.values) - 1 + v := s.values[idx] + s.values = s.values[:idx] + return v +} + +func (s *stackT[T]) Top() T { + if len(s.values) == 0 { + return s.sentinel + } + return s.values[len(s.values)-1] +} diff --git a/aws-protocols/internal/json/testdata/README.md b/aws-protocols/internal/json/testdata/README.md new file mode 100644 index 000000000..a1eb19619 --- /dev/null +++ b/aws-protocols/internal/json/testdata/README.md @@ -0,0 +1 @@ +Test cases sourced from [nst/JSONTestSuite](https://github.com/nst/JSONTestSuite). diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_number_double_huge_neg_exp.json b/aws-protocols/internal/json/testdata/test_parsing/i_number_double_huge_neg_exp.json new file mode 100644 index 000000000..ae4c7b71f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_number_double_huge_neg_exp.json @@ -0,0 +1 @@ +[123.456e-789] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_number_huge_exp.json b/aws-protocols/internal/json/testdata/test_parsing/i_number_huge_exp.json new file mode 100644 index 000000000..9b5efa236 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_number_huge_exp.json @@ -0,0 +1 @@ +[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_number_neg_int_huge_exp.json b/aws-protocols/internal/json/testdata/test_parsing/i_number_neg_int_huge_exp.json new file mode 100755 index 000000000..3abd58a5c --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_number_neg_int_huge_exp.json @@ -0,0 +1 @@ +[-1e+9999] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_number_pos_double_huge_exp.json b/aws-protocols/internal/json/testdata/test_parsing/i_number_pos_double_huge_exp.json new file mode 100755 index 000000000..e10a7eb62 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_number_pos_double_huge_exp.json @@ -0,0 +1 @@ +[1.5e+9999] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_number_real_neg_overflow.json b/aws-protocols/internal/json/testdata/test_parsing/i_number_real_neg_overflow.json new file mode 100644 index 000000000..3d628a994 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_number_real_neg_overflow.json @@ -0,0 +1 @@ +[-123123e100000] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_number_real_pos_overflow.json b/aws-protocols/internal/json/testdata/test_parsing/i_number_real_pos_overflow.json new file mode 100644 index 000000000..54d7d3dcd --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_number_real_pos_overflow.json @@ -0,0 +1 @@ +[123123e100000] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_number_real_underflow.json b/aws-protocols/internal/json/testdata/test_parsing/i_number_real_underflow.json new file mode 100644 index 000000000..c5236eb26 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_number_real_underflow.json @@ -0,0 +1 @@ +[123e-10000000] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_number_too_big_neg_int.json b/aws-protocols/internal/json/testdata/test_parsing/i_number_too_big_neg_int.json new file mode 100644 index 000000000..dfa384619 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_number_too_big_neg_int.json @@ -0,0 +1 @@ +[-123123123123123123123123123123] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_number_too_big_pos_int.json b/aws-protocols/internal/json/testdata/test_parsing/i_number_too_big_pos_int.json new file mode 100644 index 000000000..338a8c3c0 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_number_too_big_pos_int.json @@ -0,0 +1 @@ +[100000000000000000000] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_number_very_big_negative_int.json b/aws-protocols/internal/json/testdata/test_parsing/i_number_very_big_negative_int.json new file mode 100755 index 000000000..e2d9738c2 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_number_very_big_negative_int.json @@ -0,0 +1 @@ +[-237462374673276894279832749832423479823246327846] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_object_key_lone_2nd_surrogate.json b/aws-protocols/internal/json/testdata/test_parsing/i_object_key_lone_2nd_surrogate.json new file mode 100644 index 000000000..5be7ebaf9 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_object_key_lone_2nd_surrogate.json @@ -0,0 +1 @@ +{"\uDFAA":0} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_1st_surrogate_but_2nd_missing.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_1st_surrogate_but_2nd_missing.json new file mode 100644 index 000000000..3b9e37c67 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_1st_surrogate_but_2nd_missing.json @@ -0,0 +1 @@ +["\uDADA"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_1st_valid_surrogate_2nd_invalid.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_1st_valid_surrogate_2nd_invalid.json new file mode 100644 index 000000000..487592832 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_1st_valid_surrogate_2nd_invalid.json @@ -0,0 +1 @@ +["\uD888\u1234"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_UTF-16LE_with_BOM.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_UTF-16LE_with_BOM.json new file mode 100644 index 0000000000000000000000000000000000000000..2a79c0629a49133d8f715bddbef19cfb3ef025bd GIT binary patch literal 12 ScmezWFPcG#;Uy5qG5`P~Km+3d literal 0 HcmV?d00001 diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_UTF-8_invalid_sequence.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_UTF-8_invalid_sequence.json new file mode 100755 index 000000000..e2a968a15 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_UTF-8_invalid_sequence.json @@ -0,0 +1 @@ +["日шú"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_UTF8_surrogate_U+D800.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_UTF8_surrogate_U+D800.json new file mode 100644 index 000000000..916bff920 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_UTF8_surrogate_U+D800.json @@ -0,0 +1 @@ +["í €"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_incomplete_surrogate_and_escape_valid.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_incomplete_surrogate_and_escape_valid.json new file mode 100755 index 000000000..3cb11d229 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_incomplete_surrogate_and_escape_valid.json @@ -0,0 +1 @@ +["\uD800\n"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_incomplete_surrogate_pair.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_incomplete_surrogate_pair.json new file mode 100755 index 000000000..38ec23bb0 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_incomplete_surrogate_pair.json @@ -0,0 +1 @@ +["\uDd1ea"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_incomplete_surrogates_escape_valid.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_incomplete_surrogates_escape_valid.json new file mode 100755 index 000000000..c9cd6f6c3 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_incomplete_surrogates_escape_valid.json @@ -0,0 +1 @@ +["\uD800\uD800\n"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_invalid_lonely_surrogate.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_invalid_lonely_surrogate.json new file mode 100755 index 000000000..3abbd8d8d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_invalid_lonely_surrogate.json @@ -0,0 +1 @@ +["\ud800"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_invalid_surrogate.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_invalid_surrogate.json new file mode 100755 index 000000000..ffddc04f5 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_invalid_surrogate.json @@ -0,0 +1 @@ +["\ud800abc"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_invalid_utf-8.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_invalid_utf-8.json new file mode 100644 index 000000000..8e45a7eca --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_invalid_utf-8.json @@ -0,0 +1 @@ +["ÿ"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_inverted_surrogates_U+1D11E.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_inverted_surrogates_U+1D11E.json new file mode 100755 index 000000000..0d5456cc3 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_inverted_surrogates_U+1D11E.json @@ -0,0 +1 @@ +["\uDd1e\uD834"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_iso_latin_1.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_iso_latin_1.json new file mode 100644 index 000000000..9389c9823 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_iso_latin_1.json @@ -0,0 +1 @@ +["é"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_lone_second_surrogate.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_lone_second_surrogate.json new file mode 100644 index 000000000..1dbd397f3 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_lone_second_surrogate.json @@ -0,0 +1 @@ +["\uDFAA"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_lone_utf8_continuation_byte.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_lone_utf8_continuation_byte.json new file mode 100644 index 000000000..729337c0a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_lone_utf8_continuation_byte.json @@ -0,0 +1 @@ +[""] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_not_in_unicode_range.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_not_in_unicode_range.json new file mode 100644 index 000000000..df90a2916 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_not_in_unicode_range.json @@ -0,0 +1 @@ +["ô¿¿¿"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_overlong_sequence_2_bytes.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_overlong_sequence_2_bytes.json new file mode 100644 index 000000000..c8cee5e0a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_overlong_sequence_2_bytes.json @@ -0,0 +1 @@ +["À¯"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_overlong_sequence_6_bytes.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_overlong_sequence_6_bytes.json new file mode 100755 index 000000000..9a91da791 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_overlong_sequence_6_bytes.json @@ -0,0 +1 @@ +["üƒ¿¿¿¿"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_overlong_sequence_6_bytes_null.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_overlong_sequence_6_bytes_null.json new file mode 100755 index 000000000..d24fffdd9 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_overlong_sequence_6_bytes_null.json @@ -0,0 +1 @@ +["ü€€€€€"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_truncated-utf-8.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_truncated-utf-8.json new file mode 100644 index 000000000..63c7777fb --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_string_truncated-utf-8.json @@ -0,0 +1 @@ +["àÿ"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_utf16BE_no_BOM.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_utf16BE_no_BOM.json new file mode 100644 index 0000000000000000000000000000000000000000..57e5392ff6309134268c7e3ec2a9289b99ff7148 GIT binary patch literal 10 PcmZRGW>8{y3B<7g33~zN literal 0 HcmV?d00001 diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_string_utf16LE_no_BOM.json b/aws-protocols/internal/json/testdata/test_parsing/i_string_utf16LE_no_BOM.json new file mode 100644 index 0000000000000000000000000000000000000000..c49c1b25d8e5819591993d4d45e4649a46c1b3e0 GIT binary patch literal 10 Pcma!MP-1uq#IXzj3t$1} literal 0 HcmV?d00001 diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_structure_500_nested_arrays.json b/aws-protocols/internal/json/testdata/test_parsing/i_structure_500_nested_arrays.json new file mode 100644 index 000000000..711840589 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_structure_500_nested_arrays.json @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/i_structure_UTF-8_BOM_empty_object.json b/aws-protocols/internal/json/testdata/test_parsing/i_structure_UTF-8_BOM_empty_object.json new file mode 100755 index 000000000..22fdca1b2 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/i_structure_UTF-8_BOM_empty_object.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_1_true_without_comma.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_1_true_without_comma.json new file mode 100644 index 000000000..c14e3f6b1 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_1_true_without_comma.json @@ -0,0 +1 @@ +[1 true] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_a_invalid_utf8.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_a_invalid_utf8.json new file mode 100644 index 000000000..38a86e2e6 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_a_invalid_utf8.json @@ -0,0 +1 @@ +[aå] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_colon_instead_of_comma.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_colon_instead_of_comma.json new file mode 100644 index 000000000..0d02ad448 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_colon_instead_of_comma.json @@ -0,0 +1 @@ +["": 1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_comma_after_close.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_comma_after_close.json new file mode 100644 index 000000000..2ccba8d95 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_comma_after_close.json @@ -0,0 +1 @@ +[""], \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_comma_and_number.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_comma_and_number.json new file mode 100755 index 000000000..d2c84e374 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_comma_and_number.json @@ -0,0 +1 @@ +[,1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_double_comma.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_double_comma.json new file mode 100755 index 000000000..0431712bc --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_double_comma.json @@ -0,0 +1 @@ +[1,,2] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_double_extra_comma.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_double_extra_comma.json new file mode 100644 index 000000000..3f01d3129 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_double_extra_comma.json @@ -0,0 +1 @@ +["x",,] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_extra_close.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_extra_close.json new file mode 100644 index 000000000..c12f9fae1 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_extra_close.json @@ -0,0 +1 @@ +["x"]] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_extra_comma.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_extra_comma.json new file mode 100644 index 000000000..5f8ce18e4 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_extra_comma.json @@ -0,0 +1 @@ +["",] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_incomplete.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_incomplete.json new file mode 100644 index 000000000..cc65b0b51 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_incomplete.json @@ -0,0 +1 @@ +["x" \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_incomplete_invalid_value.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_incomplete_invalid_value.json new file mode 100644 index 000000000..c21a8f6cf --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_incomplete_invalid_value.json @@ -0,0 +1 @@ +[x \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_inner_array_no_comma.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_inner_array_no_comma.json new file mode 100644 index 000000000..c70b71647 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_inner_array_no_comma.json @@ -0,0 +1 @@ +[3[4]] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_invalid_utf8.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_invalid_utf8.json new file mode 100644 index 000000000..6099d3441 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_invalid_utf8.json @@ -0,0 +1 @@ +[ÿ] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_items_separated_by_semicolon.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_items_separated_by_semicolon.json new file mode 100755 index 000000000..d4bd7314c --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_items_separated_by_semicolon.json @@ -0,0 +1 @@ +[1:2] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_just_comma.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_just_comma.json new file mode 100755 index 000000000..9d7077c68 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_just_comma.json @@ -0,0 +1 @@ +[,] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_just_minus.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_just_minus.json new file mode 100755 index 000000000..29501c6ca --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_just_minus.json @@ -0,0 +1 @@ +[-] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_missing_value.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_missing_value.json new file mode 100644 index 000000000..3a6ba86f3 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_missing_value.json @@ -0,0 +1 @@ +[ , ""] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_newlines_unclosed.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_newlines_unclosed.json new file mode 100644 index 000000000..646680065 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_newlines_unclosed.json @@ -0,0 +1,3 @@ +["a", +4 +,1, \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_number_and_comma.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_number_and_comma.json new file mode 100755 index 000000000..13f6f1d18 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_number_and_comma.json @@ -0,0 +1 @@ +[1,] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_number_and_several_commas.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_number_and_several_commas.json new file mode 100755 index 000000000..0ac408cb8 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_number_and_several_commas.json @@ -0,0 +1 @@ +[1,,] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_spaces_vertical_tab_formfeed.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_spaces_vertical_tab_formfeed.json new file mode 100755 index 000000000..6cd7cf585 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_spaces_vertical_tab_formfeed.json @@ -0,0 +1 @@ +[" a"\f] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_star_inside.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_star_inside.json new file mode 100755 index 000000000..5a5194647 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_star_inside.json @@ -0,0 +1 @@ +[*] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed.json new file mode 100644 index 000000000..060733059 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed.json @@ -0,0 +1 @@ +["" \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed_trailing_comma.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed_trailing_comma.json new file mode 100644 index 000000000..6604698ff --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed_trailing_comma.json @@ -0,0 +1 @@ +[1, \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed_with_new_lines.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed_with_new_lines.json new file mode 100644 index 000000000..4f61de3fb --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed_with_new_lines.json @@ -0,0 +1,3 @@ +[1, +1 +,1 \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed_with_object_inside.json b/aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed_with_object_inside.json new file mode 100644 index 000000000..043a87e2d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_array_unclosed_with_object_inside.json @@ -0,0 +1 @@ +[{} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_incomplete_false.json b/aws-protocols/internal/json/testdata/test_parsing/n_incomplete_false.json new file mode 100644 index 000000000..eb18c6a14 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_incomplete_false.json @@ -0,0 +1 @@ +[fals] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_incomplete_null.json b/aws-protocols/internal/json/testdata/test_parsing/n_incomplete_null.json new file mode 100644 index 000000000..c18ef5385 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_incomplete_null.json @@ -0,0 +1 @@ +[nul] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_incomplete_true.json b/aws-protocols/internal/json/testdata/test_parsing/n_incomplete_true.json new file mode 100644 index 000000000..f451ac6d2 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_incomplete_true.json @@ -0,0 +1 @@ +[tru] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_multidigit_number_then_00.json b/aws-protocols/internal/json/testdata/test_parsing/n_multidigit_number_then_00.json new file mode 100644 index 0000000000000000000000000000000000000000..c22507b864f7f089c91c1eb85b1b15cc63b943a6 GIT binary patch literal 4 LcmXpsGG+h(0mJ~8 literal 0 HcmV?d00001 diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_++.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_++.json new file mode 100644 index 000000000..bdb62aaf4 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_++.json @@ -0,0 +1 @@ +[++1234] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_+1.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_+1.json new file mode 100755 index 000000000..3cbe58c92 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_+1.json @@ -0,0 +1 @@ +[+1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_+Inf.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_+Inf.json new file mode 100755 index 000000000..871ae14d5 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_+Inf.json @@ -0,0 +1 @@ +[+Inf] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_-01.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_-01.json new file mode 100755 index 000000000..0df32bac8 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_-01.json @@ -0,0 +1 @@ +[-01] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_-1.0..json b/aws-protocols/internal/json/testdata/test_parsing/n_number_-1.0..json new file mode 100755 index 000000000..7cf55a85a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_-1.0..json @@ -0,0 +1 @@ +[-1.0.] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_-2..json b/aws-protocols/internal/json/testdata/test_parsing/n_number_-2..json new file mode 100755 index 000000000..9be84365d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_-2..json @@ -0,0 +1 @@ +[-2.] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_-NaN.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_-NaN.json new file mode 100755 index 000000000..f61615d40 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_-NaN.json @@ -0,0 +1 @@ +[-NaN] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_.-1.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_.-1.json new file mode 100644 index 000000000..1c9f2dd1b --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_.-1.json @@ -0,0 +1 @@ +[.-1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_.2e-3.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_.2e-3.json new file mode 100755 index 000000000..c6c976f25 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_.2e-3.json @@ -0,0 +1 @@ +[.2e-3] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_0.1.2.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_0.1.2.json new file mode 100755 index 000000000..c83a25621 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_0.1.2.json @@ -0,0 +1 @@ +[0.1.2] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_0.3e+.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_0.3e+.json new file mode 100644 index 000000000..a55a1bfef --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_0.3e+.json @@ -0,0 +1 @@ +[0.3e+] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_0.3e.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_0.3e.json new file mode 100644 index 000000000..3dd5df4b3 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_0.3e.json @@ -0,0 +1 @@ +[0.3e] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_0.e1.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_0.e1.json new file mode 100644 index 000000000..c92c71ccb --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_0.e1.json @@ -0,0 +1 @@ +[0.e1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_0_capital_E+.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_0_capital_E+.json new file mode 100644 index 000000000..3ba2c7d6d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_0_capital_E+.json @@ -0,0 +1 @@ +[0E+] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_0_capital_E.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_0_capital_E.json new file mode 100755 index 000000000..5301840d1 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_0_capital_E.json @@ -0,0 +1 @@ +[0E] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_0e+.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_0e+.json new file mode 100644 index 000000000..8ab0bc4b8 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_0e+.json @@ -0,0 +1 @@ +[0e+] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_0e.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_0e.json new file mode 100644 index 000000000..47ec421bb --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_0e.json @@ -0,0 +1 @@ +[0e] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_1.0e+.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_1.0e+.json new file mode 100755 index 000000000..cd84b9f69 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_1.0e+.json @@ -0,0 +1 @@ +[1.0e+] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_1.0e-.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_1.0e-.json new file mode 100755 index 000000000..4eb7afa0f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_1.0e-.json @@ -0,0 +1 @@ +[1.0e-] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_1.0e.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_1.0e.json new file mode 100755 index 000000000..21753f4c7 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_1.0e.json @@ -0,0 +1 @@ +[1.0e] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_1_000.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_1_000.json new file mode 100755 index 000000000..7b18b66b3 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_1_000.json @@ -0,0 +1 @@ +[1 000.0] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_1eE2.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_1eE2.json new file mode 100755 index 000000000..4318a341d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_1eE2.json @@ -0,0 +1 @@ +[1eE2] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_2.e+3.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_2.e+3.json new file mode 100755 index 000000000..4442f394d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_2.e+3.json @@ -0,0 +1 @@ +[2.e+3] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_2.e-3.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_2.e-3.json new file mode 100755 index 000000000..a65060edf --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_2.e-3.json @@ -0,0 +1 @@ +[2.e-3] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_2.e3.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_2.e3.json new file mode 100755 index 000000000..66f7cf701 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_2.e3.json @@ -0,0 +1 @@ +[2.e3] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_9.e+.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_9.e+.json new file mode 100644 index 000000000..732a7b11c --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_9.e+.json @@ -0,0 +1 @@ +[9.e+] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_Inf.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_Inf.json new file mode 100755 index 000000000..c40c734c3 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_Inf.json @@ -0,0 +1 @@ +[Inf] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_NaN.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_NaN.json new file mode 100755 index 000000000..499231790 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_NaN.json @@ -0,0 +1 @@ +[NaN] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_U+FF11_fullwidth_digit_one.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_U+FF11_fullwidth_digit_one.json new file mode 100644 index 000000000..b14587e5e --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_U+FF11_fullwidth_digit_one.json @@ -0,0 +1 @@ +[1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_expression.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_expression.json new file mode 100644 index 000000000..76fdbc8a4 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_expression.json @@ -0,0 +1 @@ +[1+2] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_hex_1_digit.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_hex_1_digit.json new file mode 100644 index 000000000..3b214880c --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_hex_1_digit.json @@ -0,0 +1 @@ +[0x1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_hex_2_digits.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_hex_2_digits.json new file mode 100644 index 000000000..83e516ab0 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_hex_2_digits.json @@ -0,0 +1 @@ +[0x42] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_infinity.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_infinity.json new file mode 100755 index 000000000..8c2baf783 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_infinity.json @@ -0,0 +1 @@ +[Infinity] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid+-.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid+-.json new file mode 100644 index 000000000..1cce602b5 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid+-.json @@ -0,0 +1 @@ +[0e+-1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-negative-real.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-negative-real.json new file mode 100644 index 000000000..5fc3c1efb --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-negative-real.json @@ -0,0 +1 @@ +[-123.123foo] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-utf-8-in-bigger-int.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-utf-8-in-bigger-int.json new file mode 100644 index 000000000..3b97e580e --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-utf-8-in-bigger-int.json @@ -0,0 +1 @@ +[123å] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-utf-8-in-exponent.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-utf-8-in-exponent.json new file mode 100644 index 000000000..ea35d723c --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-utf-8-in-exponent.json @@ -0,0 +1 @@ +[1e1å] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-utf-8-in-int.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-utf-8-in-int.json new file mode 100644 index 000000000..371226e4c --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_invalid-utf-8-in-int.json @@ -0,0 +1 @@ +[0å] diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_minus_infinity.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_minus_infinity.json new file mode 100755 index 000000000..cf4133d22 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_minus_infinity.json @@ -0,0 +1 @@ +[-Infinity] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_minus_sign_with_trailing_garbage.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_minus_sign_with_trailing_garbage.json new file mode 100644 index 000000000..a6d8e78e7 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_minus_sign_with_trailing_garbage.json @@ -0,0 +1 @@ +[-foo] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_minus_space_1.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_minus_space_1.json new file mode 100644 index 000000000..9a5ebedf6 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_minus_space_1.json @@ -0,0 +1 @@ +[- 1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_neg_int_starting_with_zero.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_neg_int_starting_with_zero.json new file mode 100644 index 000000000..67af0960a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_neg_int_starting_with_zero.json @@ -0,0 +1 @@ +[-012] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_neg_real_without_int_part.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_neg_real_without_int_part.json new file mode 100755 index 000000000..1f2a43496 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_neg_real_without_int_part.json @@ -0,0 +1 @@ +[-.123] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_neg_with_garbage_at_end.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_neg_with_garbage_at_end.json new file mode 100644 index 000000000..2aa73119f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_neg_with_garbage_at_end.json @@ -0,0 +1 @@ +[-1x] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_real_garbage_after_e.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_real_garbage_after_e.json new file mode 100644 index 000000000..9213dfca8 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_real_garbage_after_e.json @@ -0,0 +1 @@ +[1ea] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_real_with_invalid_utf8_after_e.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_real_with_invalid_utf8_after_e.json new file mode 100644 index 000000000..1e52ef964 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_real_with_invalid_utf8_after_e.json @@ -0,0 +1 @@ +[1eå] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_real_without_fractional_part.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_real_without_fractional_part.json new file mode 100755 index 000000000..1de287cf8 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_real_without_fractional_part.json @@ -0,0 +1 @@ +[1.] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_starting_with_dot.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_starting_with_dot.json new file mode 100755 index 000000000..f682dbdce --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_starting_with_dot.json @@ -0,0 +1 @@ +[.123] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_with_alpha.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_with_alpha.json new file mode 100644 index 000000000..1e42d8182 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_with_alpha.json @@ -0,0 +1 @@ +[1.2a-3] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_with_alpha_char.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_with_alpha_char.json new file mode 100644 index 000000000..b79daccb8 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_with_alpha_char.json @@ -0,0 +1 @@ +[1.8011670033376514H-308] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_number_with_leading_zero.json b/aws-protocols/internal/json/testdata/test_parsing/n_number_with_leading_zero.json new file mode 100755 index 000000000..7106da1f3 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_number_with_leading_zero.json @@ -0,0 +1 @@ +[012] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_bad_value.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_bad_value.json new file mode 100644 index 000000000..a03a8c03b --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_bad_value.json @@ -0,0 +1 @@ +["x", truth] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_bracket_key.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_bracket_key.json new file mode 100644 index 000000000..cc443b483 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_bracket_key.json @@ -0,0 +1 @@ +{[: "x"} diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_comma_instead_of_colon.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_comma_instead_of_colon.json new file mode 100644 index 000000000..8d5637708 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_comma_instead_of_colon.json @@ -0,0 +1 @@ +{"x", null} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_double_colon.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_double_colon.json new file mode 100644 index 000000000..80e8c7b89 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_double_colon.json @@ -0,0 +1 @@ +{"x"::"b"} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_emoji.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_emoji.json new file mode 100644 index 000000000..cb4078eaa --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_emoji.json @@ -0,0 +1 @@ +{🇨🇭} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_garbage_at_end.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_garbage_at_end.json new file mode 100644 index 000000000..80c42cbad --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_garbage_at_end.json @@ -0,0 +1 @@ +{"a":"a" 123} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_key_with_single_quotes.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_key_with_single_quotes.json new file mode 100755 index 000000000..77c327599 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_key_with_single_quotes.json @@ -0,0 +1 @@ +{key: 'value'} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json new file mode 100644 index 000000000..aa2cb637c --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_lone_continuation_byte_in_key_and_trailing_comma.json @@ -0,0 +1 @@ +{"¹":"0",} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_missing_colon.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_missing_colon.json new file mode 100644 index 000000000..b98eff62d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_missing_colon.json @@ -0,0 +1 @@ +{"a" b} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_missing_key.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_missing_key.json new file mode 100755 index 000000000..b4fb0f528 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_missing_key.json @@ -0,0 +1 @@ +{:"b"} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_missing_semicolon.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_missing_semicolon.json new file mode 100755 index 000000000..e3451384f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_missing_semicolon.json @@ -0,0 +1 @@ +{"a" "b"} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_missing_value.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_missing_value.json new file mode 100644 index 000000000..3ef538a60 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_missing_value.json @@ -0,0 +1 @@ +{"a": \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_no-colon.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_no-colon.json new file mode 100644 index 000000000..f3797b357 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_no-colon.json @@ -0,0 +1 @@ +{"a" \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_non_string_key.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_non_string_key.json new file mode 100755 index 000000000..b9945b34b --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_non_string_key.json @@ -0,0 +1 @@ +{1:1} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_non_string_key_but_huge_number_instead.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_non_string_key_but_huge_number_instead.json new file mode 100755 index 000000000..b37fa86c0 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_non_string_key_but_huge_number_instead.json @@ -0,0 +1 @@ +{9999E9999:1} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_repeated_null_null.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_repeated_null_null.json new file mode 100755 index 000000000..f7d2959d0 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_repeated_null_null.json @@ -0,0 +1 @@ +{null:null,null:null} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_several_trailing_commas.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_several_trailing_commas.json new file mode 100755 index 000000000..3c9afe8dc --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_several_trailing_commas.json @@ -0,0 +1 @@ +{"id":0,,,,,} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_single_quote.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_single_quote.json new file mode 100644 index 000000000..e5cdf976a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_single_quote.json @@ -0,0 +1 @@ +{'a':0} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comma.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comma.json new file mode 100755 index 000000000..a4b025094 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comma.json @@ -0,0 +1 @@ +{"id":0,} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment.json new file mode 100644 index 000000000..a372c6553 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment.json @@ -0,0 +1 @@ +{"a":"b"}/**/ \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment_open.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment_open.json new file mode 100644 index 000000000..d557f41ca --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment_open.json @@ -0,0 +1 @@ +{"a":"b"}/**// \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment_slash_open.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment_slash_open.json new file mode 100644 index 000000000..e335136c0 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment_slash_open.json @@ -0,0 +1 @@ +{"a":"b"}// \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment_slash_open_incomplete.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment_slash_open_incomplete.json new file mode 100644 index 000000000..d892e49f1 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_trailing_comment_slash_open_incomplete.json @@ -0,0 +1 @@ +{"a":"b"}/ \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_two_commas_in_a_row.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_two_commas_in_a_row.json new file mode 100755 index 000000000..7c639ae64 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_two_commas_in_a_row.json @@ -0,0 +1 @@ +{"a":"b",,"c":"d"} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_unquoted_key.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_unquoted_key.json new file mode 100644 index 000000000..8ba137293 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_unquoted_key.json @@ -0,0 +1 @@ +{a: "b"} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_unterminated-value.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_unterminated-value.json new file mode 100644 index 000000000..7fe699a6a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_unterminated-value.json @@ -0,0 +1 @@ +{"a":"a \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_with_single_string.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_with_single_string.json new file mode 100644 index 000000000..d63f7fbb7 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_with_single_string.json @@ -0,0 +1 @@ +{ "foo" : "bar", "a" } \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_object_with_trailing_garbage.json b/aws-protocols/internal/json/testdata/test_parsing/n_object_with_trailing_garbage.json new file mode 100644 index 000000000..787c8f0a8 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_object_with_trailing_garbage.json @@ -0,0 +1 @@ +{"a":"b"}# \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_single_space.json b/aws-protocols/internal/json/testdata/test_parsing/n_single_space.json new file mode 100755 index 000000000..0519ecba6 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_single_space.json @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape.json new file mode 100644 index 000000000..acec66d8f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape.json @@ -0,0 +1 @@ +["\uD800\"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape_u.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape_u.json new file mode 100644 index 000000000..e834b05e9 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape_u.json @@ -0,0 +1 @@ +["\uD800\u"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape_u1.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape_u1.json new file mode 100644 index 000000000..a04cd3489 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape_u1.json @@ -0,0 +1 @@ +["\uD800\u1"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape_u1x.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape_u1x.json new file mode 100644 index 000000000..bfbd23409 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_1_surrogate_then_escape_u1x.json @@ -0,0 +1 @@ +["\uD800\u1x"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_accentuated_char_no_quotes.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_accentuated_char_no_quotes.json new file mode 100644 index 000000000..fd6895693 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_accentuated_char_no_quotes.json @@ -0,0 +1 @@ +[é] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_backslash_00.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_backslash_00.json new file mode 100644 index 0000000000000000000000000000000000000000..b5bf267b5d4ee922d20cec1543b66d1530ff76cf GIT binary patch literal 6 Ncma!6ieXTS1pox&0a*Y5 literal 0 HcmV?d00001 diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_escape_x.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_escape_x.json new file mode 100644 index 000000000..fae291938 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_escape_x.json @@ -0,0 +1 @@ +["\x00"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_escaped_backslash_bad.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_escaped_backslash_bad.json new file mode 100755 index 000000000..016fcb47e --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_escaped_backslash_bad.json @@ -0,0 +1 @@ +["\\\"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_escaped_ctrl_char_tab.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_escaped_ctrl_char_tab.json new file mode 100644 index 000000000..f35ea382b --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_escaped_ctrl_char_tab.json @@ -0,0 +1 @@ +["\ "] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_escaped_emoji.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_escaped_emoji.json new file mode 100644 index 000000000..a27775421 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_escaped_emoji.json @@ -0,0 +1 @@ +["\🌀"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_escape.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_escape.json new file mode 100755 index 000000000..3415c33ca --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_escape.json @@ -0,0 +1 @@ +["\"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_escaped_character.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_escaped_character.json new file mode 100755 index 000000000..0f2197ea2 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_escaped_character.json @@ -0,0 +1 @@ +["\u00A"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_surrogate.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_surrogate.json new file mode 100755 index 000000000..75504a656 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_surrogate.json @@ -0,0 +1 @@ +["\uD834\uDd"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_surrogate_escape_invalid.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_surrogate_escape_invalid.json new file mode 100755 index 000000000..bd9656060 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_incomplete_surrogate_escape_invalid.json @@ -0,0 +1 @@ +["\uD800\uD800\x"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_invalid-utf-8-in-escape.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_invalid-utf-8-in-escape.json new file mode 100644 index 000000000..0c4300643 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_invalid-utf-8-in-escape.json @@ -0,0 +1 @@ +["\uå"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_invalid_backslash_esc.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_invalid_backslash_esc.json new file mode 100755 index 000000000..d1eb60921 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_invalid_backslash_esc.json @@ -0,0 +1 @@ +["\a"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_invalid_unicode_escape.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_invalid_unicode_escape.json new file mode 100644 index 000000000..7608cb6ba --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_invalid_unicode_escape.json @@ -0,0 +1 @@ +["\uqqqq"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_invalid_utf8_after_escape.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_invalid_utf8_after_escape.json new file mode 100644 index 000000000..2f757a25b --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_invalid_utf8_after_escape.json @@ -0,0 +1 @@ +["\å"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_leading_uescaped_thinspace.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_leading_uescaped_thinspace.json new file mode 100755 index 000000000..7b297c636 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_leading_uescaped_thinspace.json @@ -0,0 +1 @@ +[\u0020"asd"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_no_quotes_with_bad_escape.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_no_quotes_with_bad_escape.json new file mode 100644 index 000000000..01bc70aba --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_no_quotes_with_bad_escape.json @@ -0,0 +1 @@ +[\n] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_single_doublequote.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_single_doublequote.json new file mode 100755 index 000000000..9d68933c4 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_single_doublequote.json @@ -0,0 +1 @@ +" \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_single_quote.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_single_quote.json new file mode 100644 index 000000000..caff239bf --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_single_quote.json @@ -0,0 +1 @@ +['single quote'] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_single_string_no_double_quotes.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_single_string_no_double_quotes.json new file mode 100755 index 000000000..f2ba8f84a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_single_string_no_double_quotes.json @@ -0,0 +1 @@ +abc \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_start_escape_unclosed.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_start_escape_unclosed.json new file mode 100644 index 000000000..db62a46fc --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_start_escape_unclosed.json @@ -0,0 +1 @@ +["\ \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_unescaped_ctrl_char.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_unescaped_ctrl_char.json new file mode 100755 index 0000000000000000000000000000000000000000..9f21348071d3d736bdd469f159cea674c09237af GIT binary patch literal 7 Ocma!6N@Pe>iUj}$`2oKG literal 0 HcmV?d00001 diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_unescaped_newline.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_unescaped_newline.json new file mode 100644 index 000000000..700d36086 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_unescaped_newline.json @@ -0,0 +1,2 @@ +["new +line"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_unescaped_tab.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_unescaped_tab.json new file mode 100644 index 000000000..160264a2d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_unescaped_tab.json @@ -0,0 +1 @@ +[" "] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_unicode_CapitalU.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_unicode_CapitalU.json new file mode 100644 index 000000000..17332bb17 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_unicode_CapitalU.json @@ -0,0 +1 @@ +"\UA66D" \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_string_with_trailing_garbage.json b/aws-protocols/internal/json/testdata/test_parsing/n_string_with_trailing_garbage.json new file mode 100644 index 000000000..efe3bd272 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_string_with_trailing_garbage.json @@ -0,0 +1 @@ +""x \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_100000_opening_arrays.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_100000_opening_arrays.json new file mode 100644 index 000000000..a4823eecc --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_100000_opening_arrays.json @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_U+2060_word_joined.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_U+2060_word_joined.json new file mode 100644 index 000000000..81156a699 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_U+2060_word_joined.json @@ -0,0 +1 @@ +[â ] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_UTF8_BOM_no_data.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_UTF8_BOM_no_data.json new file mode 100755 index 000000000..5f282702b --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_UTF8_BOM_no_data.json @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_angle_bracket_..json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_angle_bracket_..json new file mode 100755 index 000000000..a56fef0b0 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_angle_bracket_..json @@ -0,0 +1 @@ +<.> \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_angle_bracket_null.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_angle_bracket_null.json new file mode 100755 index 000000000..617f26254 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_angle_bracket_null.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_array_trailing_garbage.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_array_trailing_garbage.json new file mode 100644 index 000000000..5a745e6f3 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_array_trailing_garbage.json @@ -0,0 +1 @@ +[1]x \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_array_with_extra_array_close.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_array_with_extra_array_close.json new file mode 100755 index 000000000..6cfb1398d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_array_with_extra_array_close.json @@ -0,0 +1 @@ +[1]] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_array_with_unclosed_string.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_array_with_unclosed_string.json new file mode 100755 index 000000000..ba6b1788b --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_array_with_unclosed_string.json @@ -0,0 +1 @@ +["asd] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_ascii-unicode-identifier.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_ascii-unicode-identifier.json new file mode 100644 index 000000000..ef2ab62fe --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_ascii-unicode-identifier.json @@ -0,0 +1 @@ +aÃ¥ \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_capitalized_True.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_capitalized_True.json new file mode 100755 index 000000000..7cd88469a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_capitalized_True.json @@ -0,0 +1 @@ +[True] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_close_unopened_array.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_close_unopened_array.json new file mode 100755 index 000000000..d2af0c646 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_close_unopened_array.json @@ -0,0 +1 @@ +1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_comma_instead_of_closing_brace.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_comma_instead_of_closing_brace.json new file mode 100644 index 000000000..ac61b8200 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_comma_instead_of_closing_brace.json @@ -0,0 +1 @@ +{"x": true, \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_double_array.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_double_array.json new file mode 100755 index 000000000..058d1626e --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_double_array.json @@ -0,0 +1 @@ +[][] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_end_array.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_end_array.json new file mode 100644 index 000000000..54caf60b1 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_end_array.json @@ -0,0 +1 @@ +] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_incomplete_UTF8_BOM.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_incomplete_UTF8_BOM.json new file mode 100755 index 000000000..bfcdd514f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_incomplete_UTF8_BOM.json @@ -0,0 +1 @@ +ï»{} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_lone-invalid-utf-8.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_lone-invalid-utf-8.json new file mode 100644 index 000000000..8b1296cad --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_lone-invalid-utf-8.json @@ -0,0 +1 @@ +å \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_lone-open-bracket.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_lone-open-bracket.json new file mode 100644 index 000000000..8e2f0bef1 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_lone-open-bracket.json @@ -0,0 +1 @@ +[ \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_no_data.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_no_data.json new file mode 100644 index 000000000..e69de29bb diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_null-byte-outside-string.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_null-byte-outside-string.json new file mode 100644 index 0000000000000000000000000000000000000000..326db14422a756e9bd39221edf37b844cb8471c7 GIT binary patch literal 3 Kcma!Mhy?%vaR9jh literal 0 HcmV?d00001 diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_number_with_trailing_garbage.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_number_with_trailing_garbage.json new file mode 100644 index 000000000..0746539d2 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_number_with_trailing_garbage.json @@ -0,0 +1 @@ +2@ \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_object_followed_by_closing_object.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_object_followed_by_closing_object.json new file mode 100644 index 000000000..aa9ebaec5 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_object_followed_by_closing_object.json @@ -0,0 +1 @@ +{}} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_object_unclosed_no_value.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_object_unclosed_no_value.json new file mode 100644 index 000000000..17d045147 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_object_unclosed_no_value.json @@ -0,0 +1 @@ +{"": \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_object_with_comment.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_object_with_comment.json new file mode 100644 index 000000000..ed1b569b7 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_object_with_comment.json @@ -0,0 +1 @@ +{"a":/*comment*/"b"} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_object_with_trailing_garbage.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_object_with_trailing_garbage.json new file mode 100644 index 000000000..9ca2336d7 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_object_with_trailing_garbage.json @@ -0,0 +1 @@ +{"a": true} "x" \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_apostrophe.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_apostrophe.json new file mode 100644 index 000000000..8bebe3af0 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_apostrophe.json @@ -0,0 +1 @@ +[' \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_comma.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_comma.json new file mode 100644 index 000000000..6295fdc36 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_comma.json @@ -0,0 +1 @@ +[, \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_object.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_object.json new file mode 100644 index 000000000..e870445b2 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_object.json @@ -0,0 +1 @@ +[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"": diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_open_object.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_open_object.json new file mode 100644 index 000000000..7a63c8c57 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_open_object.json @@ -0,0 +1 @@ +[{ \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_open_string.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_open_string.json new file mode 100644 index 000000000..9822a6baf --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_open_string.json @@ -0,0 +1 @@ +["a \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_string.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_string.json new file mode 100644 index 000000000..42a619362 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_array_string.json @@ -0,0 +1 @@ +["a" \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object.json new file mode 100644 index 000000000..81750b96f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object.json @@ -0,0 +1 @@ +{ \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_close_array.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_close_array.json new file mode 100755 index 000000000..eebc700a1 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_close_array.json @@ -0,0 +1 @@ +{] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_comma.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_comma.json new file mode 100644 index 000000000..47bc9106f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_comma.json @@ -0,0 +1 @@ +{, \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_open_array.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_open_array.json new file mode 100644 index 000000000..381ede5de --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_open_array.json @@ -0,0 +1 @@ +{[ \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_open_string.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_open_string.json new file mode 100644 index 000000000..328c30cd7 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_open_string.json @@ -0,0 +1 @@ +{"a \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_string_with_apostrophes.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_string_with_apostrophes.json new file mode 100644 index 000000000..9dba17090 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_object_string_with_apostrophes.json @@ -0,0 +1 @@ +{'a' \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_open.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_open.json new file mode 100644 index 000000000..841fd5f86 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_open_open.json @@ -0,0 +1 @@ +["\{["\{["\{["\{ \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_single_eacute.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_single_eacute.json new file mode 100644 index 000000000..92a39f398 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_single_eacute.json @@ -0,0 +1 @@ +é \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_single_star.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_single_star.json new file mode 100755 index 000000000..f59ec20aa --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_single_star.json @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_trailing_#.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_trailing_#.json new file mode 100644 index 000000000..898611087 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_trailing_#.json @@ -0,0 +1 @@ +{"a":"b"}#{} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_uescaped_LF_before_string.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_uescaped_LF_before_string.json new file mode 100755 index 000000000..df2f0f242 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_uescaped_LF_before_string.json @@ -0,0 +1 @@ +[\u000A""] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array.json new file mode 100755 index 000000000..11209515c --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array.json @@ -0,0 +1 @@ +[1 \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array_partial_null.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array_partial_null.json new file mode 100644 index 000000000..0d591762c --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array_partial_null.json @@ -0,0 +1 @@ +[ false, nul \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array_unfinished_false.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array_unfinished_false.json new file mode 100644 index 000000000..a2ff8504a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array_unfinished_false.json @@ -0,0 +1 @@ +[ true, fals \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array_unfinished_true.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array_unfinished_true.json new file mode 100644 index 000000000..3149e8f5a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_array_unfinished_true.json @@ -0,0 +1 @@ +[ false, tru \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_object.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_object.json new file mode 100755 index 000000000..694d69dbd --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_unclosed_object.json @@ -0,0 +1 @@ +{"asd":"asd" \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_unicode-identifier.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_unicode-identifier.json new file mode 100644 index 000000000..7284aea33 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_unicode-identifier.json @@ -0,0 +1 @@ +Ã¥ \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_whitespace_U+2060_word_joiner.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_whitespace_U+2060_word_joiner.json new file mode 100755 index 000000000..81156a699 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_whitespace_U+2060_word_joiner.json @@ -0,0 +1 @@ +[â ] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/n_structure_whitespace_formfeed.json b/aws-protocols/internal/json/testdata/test_parsing/n_structure_whitespace_formfeed.json new file mode 100755 index 000000000..a9ea535d1 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/n_structure_whitespace_formfeed.json @@ -0,0 +1 @@ +[ ] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_array_arraysWithSpaces.json b/aws-protocols/internal/json/testdata/test_parsing/y_array_arraysWithSpaces.json new file mode 100755 index 000000000..582290798 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_array_arraysWithSpaces.json @@ -0,0 +1 @@ +[[] ] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_array_empty-string.json b/aws-protocols/internal/json/testdata/test_parsing/y_array_empty-string.json new file mode 100644 index 000000000..93b6be2bc --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_array_empty-string.json @@ -0,0 +1 @@ +[""] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_array_empty.json b/aws-protocols/internal/json/testdata/test_parsing/y_array_empty.json new file mode 100755 index 000000000..0637a088a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_array_empty.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_array_ending_with_newline.json b/aws-protocols/internal/json/testdata/test_parsing/y_array_ending_with_newline.json new file mode 100755 index 000000000..eac5f7b46 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_array_ending_with_newline.json @@ -0,0 +1 @@ +["a"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_array_false.json b/aws-protocols/internal/json/testdata/test_parsing/y_array_false.json new file mode 100644 index 000000000..67b2f0760 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_array_false.json @@ -0,0 +1 @@ +[false] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_array_heterogeneous.json b/aws-protocols/internal/json/testdata/test_parsing/y_array_heterogeneous.json new file mode 100755 index 000000000..d3c1e2648 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_array_heterogeneous.json @@ -0,0 +1 @@ +[null, 1, "1", {}] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_array_null.json b/aws-protocols/internal/json/testdata/test_parsing/y_array_null.json new file mode 100644 index 000000000..500db4a86 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_array_null.json @@ -0,0 +1 @@ +[null] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_array_with_1_and_newline.json b/aws-protocols/internal/json/testdata/test_parsing/y_array_with_1_and_newline.json new file mode 100644 index 000000000..994825500 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_array_with_1_and_newline.json @@ -0,0 +1,2 @@ +[1 +] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_array_with_leading_space.json b/aws-protocols/internal/json/testdata/test_parsing/y_array_with_leading_space.json new file mode 100755 index 000000000..18bfe6422 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_array_with_leading_space.json @@ -0,0 +1 @@ + [1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_array_with_several_null.json b/aws-protocols/internal/json/testdata/test_parsing/y_array_with_several_null.json new file mode 100755 index 000000000..99f6c5d1d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_array_with_several_null.json @@ -0,0 +1 @@ +[1,null,null,null,2] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_array_with_trailing_space.json b/aws-protocols/internal/json/testdata/test_parsing/y_array_with_trailing_space.json new file mode 100755 index 000000000..de9e7a944 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_array_with_trailing_space.json @@ -0,0 +1 @@ +[2] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number.json b/aws-protocols/internal/json/testdata/test_parsing/y_number.json new file mode 100644 index 000000000..e5f5cc334 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number.json @@ -0,0 +1 @@ +[123e65] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_0e+1.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_0e+1.json new file mode 100755 index 000000000..d1d396706 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_0e+1.json @@ -0,0 +1 @@ +[0e+1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_0e1.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_0e1.json new file mode 100755 index 000000000..3283a7936 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_0e1.json @@ -0,0 +1 @@ +[0e1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_after_space.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_after_space.json new file mode 100644 index 000000000..623570d96 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_after_space.json @@ -0,0 +1 @@ +[ 4] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_double_close_to_zero.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_double_close_to_zero.json new file mode 100755 index 000000000..96555ff78 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_double_close_to_zero.json @@ -0,0 +1 @@ +[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_int_with_exp.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_int_with_exp.json new file mode 100755 index 000000000..a4ca9e754 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_int_with_exp.json @@ -0,0 +1 @@ +[20e1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_minus_zero.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_minus_zero.json new file mode 100755 index 000000000..37af1312a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_minus_zero.json @@ -0,0 +1 @@ +[-0] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_negative_int.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_negative_int.json new file mode 100644 index 000000000..8e30f8bd9 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_negative_int.json @@ -0,0 +1 @@ +[-123] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_negative_one.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_negative_one.json new file mode 100644 index 000000000..99d21a2a0 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_negative_one.json @@ -0,0 +1 @@ +[-1] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_negative_zero.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_negative_zero.json new file mode 100644 index 000000000..37af1312a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_negative_zero.json @@ -0,0 +1 @@ +[-0] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_real_capital_e.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_capital_e.json new file mode 100644 index 000000000..6edbdfcb1 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_capital_e.json @@ -0,0 +1 @@ +[1E22] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_real_capital_e_neg_exp.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_capital_e_neg_exp.json new file mode 100644 index 000000000..0a01bd3ef --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_capital_e_neg_exp.json @@ -0,0 +1 @@ +[1E-2] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_real_capital_e_pos_exp.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_capital_e_pos_exp.json new file mode 100644 index 000000000..5a8fc0972 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_capital_e_pos_exp.json @@ -0,0 +1 @@ +[1E+2] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_real_exponent.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_exponent.json new file mode 100644 index 000000000..da2522d61 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_exponent.json @@ -0,0 +1 @@ +[123e45] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_real_fraction_exponent.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_fraction_exponent.json new file mode 100644 index 000000000..3944a7a45 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_fraction_exponent.json @@ -0,0 +1 @@ +[123.456e78] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_real_neg_exp.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_neg_exp.json new file mode 100644 index 000000000..ca40d3c25 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_neg_exp.json @@ -0,0 +1 @@ +[1e-2] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_real_pos_exponent.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_pos_exponent.json new file mode 100644 index 000000000..343601d51 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_real_pos_exponent.json @@ -0,0 +1 @@ +[1e+2] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_simple_int.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_simple_int.json new file mode 100644 index 000000000..e47f69afc --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_simple_int.json @@ -0,0 +1 @@ +[123] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_number_simple_real.json b/aws-protocols/internal/json/testdata/test_parsing/y_number_simple_real.json new file mode 100644 index 000000000..b02878e5f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_number_simple_real.json @@ -0,0 +1 @@ +[123.456789] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_object.json b/aws-protocols/internal/json/testdata/test_parsing/y_object.json new file mode 100755 index 000000000..78262eda3 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_object.json @@ -0,0 +1 @@ +{"asd":"sdf", "dfg":"fgh"} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_object_basic.json b/aws-protocols/internal/json/testdata/test_parsing/y_object_basic.json new file mode 100755 index 000000000..646bbe7bb --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_object_basic.json @@ -0,0 +1 @@ +{"asd":"sdf"} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_object_duplicated_key.json b/aws-protocols/internal/json/testdata/test_parsing/y_object_duplicated_key.json new file mode 100755 index 000000000..bbc2e1ce4 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_object_duplicated_key.json @@ -0,0 +1 @@ +{"a":"b","a":"c"} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_object_duplicated_key_and_value.json b/aws-protocols/internal/json/testdata/test_parsing/y_object_duplicated_key_and_value.json new file mode 100755 index 000000000..211581c20 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_object_duplicated_key_and_value.json @@ -0,0 +1 @@ +{"a":"b","a":"b"} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_object_empty.json b/aws-protocols/internal/json/testdata/test_parsing/y_object_empty.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_object_empty.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_object_empty_key.json b/aws-protocols/internal/json/testdata/test_parsing/y_object_empty_key.json new file mode 100755 index 000000000..c0013d3b8 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_object_empty_key.json @@ -0,0 +1 @@ +{"":0} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_object_escaped_null_in_key.json b/aws-protocols/internal/json/testdata/test_parsing/y_object_escaped_null_in_key.json new file mode 100644 index 000000000..593f0f67f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_object_escaped_null_in_key.json @@ -0,0 +1 @@ +{"foo\u0000bar": 42} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_object_extreme_numbers.json b/aws-protocols/internal/json/testdata/test_parsing/y_object_extreme_numbers.json new file mode 100644 index 000000000..a0d3531c3 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_object_extreme_numbers.json @@ -0,0 +1 @@ +{ "min": -1.0e+28, "max": 1.0e+28 } \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_object_long_strings.json b/aws-protocols/internal/json/testdata/test_parsing/y_object_long_strings.json new file mode 100644 index 000000000..bdc4a0871 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_object_long_strings.json @@ -0,0 +1 @@ +{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_object_simple.json b/aws-protocols/internal/json/testdata/test_parsing/y_object_simple.json new file mode 100644 index 000000000..dacac917f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_object_simple.json @@ -0,0 +1 @@ +{"a":[]} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_object_string_unicode.json b/aws-protocols/internal/json/testdata/test_parsing/y_object_string_unicode.json new file mode 100644 index 000000000..8effdb297 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_object_string_unicode.json @@ -0,0 +1 @@ +{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" } \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_object_with_newlines.json b/aws-protocols/internal/json/testdata/test_parsing/y_object_with_newlines.json new file mode 100644 index 000000000..246ec6b34 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_object_with_newlines.json @@ -0,0 +1,3 @@ +{ +"a": "b" +} \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_1_2_3_bytes_UTF-8_sequences.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_1_2_3_bytes_UTF-8_sequences.json new file mode 100755 index 000000000..9967ddeb8 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_1_2_3_bytes_UTF-8_sequences.json @@ -0,0 +1 @@ +["\u0060\u012a\u12AB"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_accepted_surrogate_pair.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_accepted_surrogate_pair.json new file mode 100755 index 000000000..996875cc8 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_accepted_surrogate_pair.json @@ -0,0 +1 @@ +["\uD801\udc37"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_accepted_surrogate_pairs.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_accepted_surrogate_pairs.json new file mode 100755 index 000000000..3401021ec --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_accepted_surrogate_pairs.json @@ -0,0 +1 @@ +["\ud83d\ude39\ud83d\udc8d"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_allowed_escapes.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_allowed_escapes.json new file mode 100644 index 000000000..7f495532f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_allowed_escapes.json @@ -0,0 +1 @@ +["\"\\\/\b\f\n\r\t"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_backslash_and_u_escaped_zero.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_backslash_and_u_escaped_zero.json new file mode 100755 index 000000000..d4439eda7 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_backslash_and_u_escaped_zero.json @@ -0,0 +1 @@ +["\\u0000"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_backslash_doublequotes.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_backslash_doublequotes.json new file mode 100644 index 000000000..ae03243b6 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_backslash_doublequotes.json @@ -0,0 +1 @@ +["\""] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_comments.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_comments.json new file mode 100644 index 000000000..2260c20c2 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_comments.json @@ -0,0 +1 @@ +["a/*b*/c/*d//e"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_double_escape_a.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_double_escape_a.json new file mode 100644 index 000000000..6715d6f40 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_double_escape_a.json @@ -0,0 +1 @@ +["\\a"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_double_escape_n.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_double_escape_n.json new file mode 100644 index 000000000..44ca56c4d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_double_escape_n.json @@ -0,0 +1 @@ +["\\n"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_escaped_control_character.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_escaped_control_character.json new file mode 100644 index 000000000..5b014a9c2 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_escaped_control_character.json @@ -0,0 +1 @@ +["\u0012"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_escaped_noncharacter.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_escaped_noncharacter.json new file mode 100755 index 000000000..2ff52e2c5 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_escaped_noncharacter.json @@ -0,0 +1 @@ +["\uFFFF"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_in_array.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_in_array.json new file mode 100755 index 000000000..21d7ae4cd --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_in_array.json @@ -0,0 +1 @@ +["asd"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_in_array_with_leading_space.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_in_array_with_leading_space.json new file mode 100755 index 000000000..9e1887c1e --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_in_array_with_leading_space.json @@ -0,0 +1 @@ +[ "asd"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_last_surrogates_1_and_2.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_last_surrogates_1_and_2.json new file mode 100644 index 000000000..3919cef76 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_last_surrogates_1_and_2.json @@ -0,0 +1 @@ +["\uDBFF\uDFFF"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_nbsp_uescaped.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_nbsp_uescaped.json new file mode 100644 index 000000000..2085ab1a1 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_nbsp_uescaped.json @@ -0,0 +1 @@ +["new\u00A0line"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json new file mode 100755 index 000000000..059e4d9dd --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_nonCharacterInUTF-8_U+10FFFF.json @@ -0,0 +1 @@ +["ô¿¿"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_nonCharacterInUTF-8_U+FFFF.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_nonCharacterInUTF-8_U+FFFF.json new file mode 100755 index 000000000..4c913bd41 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_nonCharacterInUTF-8_U+FFFF.json @@ -0,0 +1 @@ +["ï¿¿"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_null_escape.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_null_escape.json new file mode 100644 index 000000000..c1ad84404 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_null_escape.json @@ -0,0 +1 @@ +["\u0000"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_one-byte-utf-8.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_one-byte-utf-8.json new file mode 100644 index 000000000..157185923 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_one-byte-utf-8.json @@ -0,0 +1 @@ +["\u002c"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_pi.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_pi.json new file mode 100644 index 000000000..9df11ae88 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_pi.json @@ -0,0 +1 @@ +["Ï€"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_reservedCharacterInUTF-8_U+1BFFF.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_reservedCharacterInUTF-8_U+1BFFF.json new file mode 100755 index 000000000..10a33a171 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_reservedCharacterInUTF-8_U+1BFFF.json @@ -0,0 +1 @@ +["𛿿"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_simple_ascii.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_simple_ascii.json new file mode 100644 index 000000000..8cadf7d05 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_simple_ascii.json @@ -0,0 +1 @@ +["asd "] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_space.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_space.json new file mode 100644 index 000000000..efd782cc3 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_space.json @@ -0,0 +1 @@ +" " \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json new file mode 100755 index 000000000..7620b6655 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF.json @@ -0,0 +1 @@ +["\uD834\uDd1e"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_three-byte-utf-8.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_three-byte-utf-8.json new file mode 100644 index 000000000..108f1d67d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_three-byte-utf-8.json @@ -0,0 +1 @@ +["\u0821"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_two-byte-utf-8.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_two-byte-utf-8.json new file mode 100644 index 000000000..461503c31 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_two-byte-utf-8.json @@ -0,0 +1 @@ +["\u0123"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_u+2028_line_sep.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_u+2028_line_sep.json new file mode 100755 index 000000000..897b6021a --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_u+2028_line_sep.json @@ -0,0 +1 @@ +["
"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_u+2029_par_sep.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_u+2029_par_sep.json new file mode 100755 index 000000000..8cd998c89 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_u+2029_par_sep.json @@ -0,0 +1 @@ +["
"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_uEscape.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_uEscape.json new file mode 100755 index 000000000..f7b41a02f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_uEscape.json @@ -0,0 +1 @@ +["\u0061\u30af\u30EA\u30b9"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_uescaped_newline.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_uescaped_newline.json new file mode 100644 index 000000000..3a5a220b6 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_uescaped_newline.json @@ -0,0 +1 @@ +["new\u000Aline"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_unescaped_char_delete.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_unescaped_char_delete.json new file mode 100755 index 000000000..7d064f498 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_unescaped_char_delete.json @@ -0,0 +1 @@ +[""] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode.json new file mode 100644 index 000000000..3598095b7 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode.json @@ -0,0 +1 @@ +["\uA66D"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_unicodeEscapedBackslash.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicodeEscapedBackslash.json new file mode 100755 index 000000000..0bb3b51e7 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicodeEscapedBackslash.json @@ -0,0 +1 @@ +["\u005C"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_2.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_2.json new file mode 100644 index 000000000..a7dcb9768 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_2.json @@ -0,0 +1 @@ +["â‚㈴â‚"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+10FFFE_nonchar.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+10FFFE_nonchar.json new file mode 100644 index 000000000..9a8370b96 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+10FFFE_nonchar.json @@ -0,0 +1 @@ +["\uDBFF\uDFFE"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+1FFFE_nonchar.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+1FFFE_nonchar.json new file mode 100644 index 000000000..c51f8ae45 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+1FFFE_nonchar.json @@ -0,0 +1 @@ +["\uD83F\uDFFE"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json new file mode 100644 index 000000000..626d5f815 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+200B_ZERO_WIDTH_SPACE.json @@ -0,0 +1 @@ +["\u200B"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+2064_invisible_plus.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+2064_invisible_plus.json new file mode 100644 index 000000000..1e23972c6 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+2064_invisible_plus.json @@ -0,0 +1 @@ +["\u2064"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+FDD0_nonchar.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+FDD0_nonchar.json new file mode 100644 index 000000000..18ef151b4 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+FDD0_nonchar.json @@ -0,0 +1 @@ +["\uFDD0"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+FFFE_nonchar.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+FFFE_nonchar.json new file mode 100644 index 000000000..13d261fda --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_U+FFFE_nonchar.json @@ -0,0 +1 @@ +["\uFFFE"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_escaped_double_quote.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_escaped_double_quote.json new file mode 100755 index 000000000..4e6257856 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_unicode_escaped_double_quote.json @@ -0,0 +1 @@ +["\u0022"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_utf8.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_utf8.json new file mode 100644 index 000000000..40878435f --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_utf8.json @@ -0,0 +1 @@ +["€ð„ž"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_string_with_del_character.json b/aws-protocols/internal/json/testdata/test_parsing/y_string_with_del_character.json new file mode 100755 index 000000000..8bd24907d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_string_with_del_character.json @@ -0,0 +1 @@ +["aa"] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_false.json b/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_false.json new file mode 100644 index 000000000..02e4a84d6 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_false.json @@ -0,0 +1 @@ +false \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_int.json b/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_int.json new file mode 100755 index 000000000..f70d7bba4 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_int.json @@ -0,0 +1 @@ +42 \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_negative_real.json b/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_negative_real.json new file mode 100755 index 000000000..b5135a207 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_negative_real.json @@ -0,0 +1 @@ +-0.1 \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_null.json b/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_null.json new file mode 100644 index 000000000..ec747fa47 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_null.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_string.json b/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_string.json new file mode 100755 index 000000000..b6e982ca9 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_string.json @@ -0,0 +1 @@ +"asd" \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_true.json b/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_true.json new file mode 100755 index 000000000..f32a5804e --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_structure_lonely_true.json @@ -0,0 +1 @@ +true \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_structure_string_empty.json b/aws-protocols/internal/json/testdata/test_parsing/y_structure_string_empty.json new file mode 100644 index 000000000..3cc762b55 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_structure_string_empty.json @@ -0,0 +1 @@ +"" \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_structure_trailing_newline.json b/aws-protocols/internal/json/testdata/test_parsing/y_structure_trailing_newline.json new file mode 100644 index 000000000..0c3426d4c --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_structure_trailing_newline.json @@ -0,0 +1 @@ +["a"] diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_structure_true_in_array.json b/aws-protocols/internal/json/testdata/test_parsing/y_structure_true_in_array.json new file mode 100644 index 000000000..de601e305 --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_structure_true_in_array.json @@ -0,0 +1 @@ +[true] \ No newline at end of file diff --git a/aws-protocols/internal/json/testdata/test_parsing/y_structure_whitespace_array.json b/aws-protocols/internal/json/testdata/test_parsing/y_structure_whitespace_array.json new file mode 100644 index 000000000..2bedf7f2d --- /dev/null +++ b/aws-protocols/internal/json/testdata/test_parsing/y_structure_whitespace_array.json @@ -0,0 +1 @@ + [] \ No newline at end of file From 46bf285d506983426dc05a05873cb82facff222d Mon Sep 17 00:00:00 2001 From: Luc Talatinian <102624213+lucix-aws@users.noreply.github.com> Date: Tue, 19 May 2026 11:16:21 -0400 Subject: [PATCH 23/38] drop readptr shenanigans (#664) --- .../internal/httpbinding/deserializer.go | 143 ++---------------- .../internal/json/shape_deserializer.go | 73 +++------ .../internal/xml/shape_deserializer.go | 63 ++------ .../codegen/serde2/StructureDeserializer.java | 29 ++-- eventstream/deserializer.go | 103 ++----------- serde.go | 53 ++----- .../internal/cbor/shape_deserializer.go | 72 +++------ 7 files changed, 102 insertions(+), 434 deletions(-) diff --git a/aws-protocols/internal/httpbinding/deserializer.go b/aws-protocols/internal/httpbinding/deserializer.go index 760ef99c8..26a187d31 100644 --- a/aws-protocols/internal/httpbinding/deserializer.go +++ b/aws-protocols/internal/httpbinding/deserializer.go @@ -3,6 +3,7 @@ package httpbinding import ( "encoding/base64" "fmt" + "math/big" "net/http" "strconv" "strings" @@ -149,26 +150,6 @@ func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { return d.body.ReadString(s, v) } -// ReadStringPtr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadStringPtr(s *smithy.Schema, v **string) error { - if d.inBindings { - if _, ok := isHTTPHeader(s); ok { - hv, err := d.readHeaderString(s) - if err != nil { - return err - } - *v = &hv - return nil - } - if _, ok := smithy.SchemaTrait[*traits.HTTPPayload](s); ok { - val := string(d.payload) - *v = &val - return nil - } - } - return d.body.ReadStringPtr(s, v) -} - // ReadBool implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { if !d.inBindings { @@ -193,19 +174,6 @@ func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { return nil } -// ReadBoolPtr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadBoolPtr(s *smithy.Schema, v **bool) error { - if !d.inBindings { - return d.body.ReadBoolPtr(s, v) - } - var vv bool - if err := d.ReadBool(s, &vv); err != nil { - return err - } - *v = &vv - return nil -} - // ReadInt8 implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadInt8(s *smithy.Schema, v *int8) error { if !d.inBindings { @@ -214,19 +182,6 @@ func (d *ShapeDeserializer) ReadInt8(s *smithy.Schema, v *int8) error { return readHeaderInt[int8](d, s, v) } -// ReadInt8Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt8Ptr(s *smithy.Schema, v **int8) error { - if !d.inBindings { - return d.body.ReadInt8Ptr(s, v) - } - var vv int8 - if err := d.ReadInt8(s, &vv); err != nil { - return err - } - *v = &vv - return nil -} - // ReadInt16 implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadInt16(s *smithy.Schema, v *int16) error { if !d.inBindings { @@ -235,19 +190,6 @@ func (d *ShapeDeserializer) ReadInt16(s *smithy.Schema, v *int16) error { return readHeaderInt[int16](d, s, v) } -// ReadInt16Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt16Ptr(s *smithy.Schema, v **int16) error { - if !d.inBindings { - return d.body.ReadInt16Ptr(s, v) - } - var vv int16 - if err := d.ReadInt16(s, &vv); err != nil { - return err - } - *v = &vv - return nil -} - // ReadInt32 implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadInt32(s *smithy.Schema, v *int32) error { if !d.inBindings { @@ -266,19 +208,6 @@ func (d *ShapeDeserializer) ReadInt32(s *smithy.Schema, v *int32) error { return readHeaderInt[int32](d, s, v) } -// ReadInt32Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt32Ptr(s *smithy.Schema, v **int32) error { - if !d.inBindings { - return d.body.ReadInt32Ptr(s, v) - } - var vv int32 - if err := d.ReadInt32(s, &vv); err != nil { - return err - } - *v = &vv - return nil -} - // ReadInt64 implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { if !d.inBindings { @@ -287,19 +216,6 @@ func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { return readHeaderInt[int64](d, s, v) } -// ReadInt64Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt64Ptr(s *smithy.Schema, v **int64) error { - if !d.inBindings { - return d.body.ReadInt64Ptr(s, v) - } - var vv int64 - if err := d.ReadInt64(s, &vv); err != nil { - return err - } - *v = &vv - return nil -} - // ReadFloat32 implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error { if !d.inBindings { @@ -308,19 +224,6 @@ func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error { return readHeaderFloat[float32](d, s, v) } -// ReadFloat32Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadFloat32Ptr(s *smithy.Schema, v **float32) error { - if !d.inBindings { - return d.body.ReadFloat32Ptr(s, v) - } - var vv float32 - if err := d.ReadFloat32(s, &vv); err != nil { - return err - } - *v = &vv - return nil -} - // ReadFloat64 implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { if !d.inBindings { @@ -329,19 +232,6 @@ func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { return readHeaderFloat[float64](d, s, v) } -// ReadFloat64Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadFloat64Ptr(s *smithy.Schema, v **float64) error { - if !d.inBindings { - return d.body.ReadFloat64Ptr(s, v) - } - var vv float64 - if err := d.ReadFloat64(s, &vv); err != nil { - return err - } - *v = &vv - return nil -} - // ReadTime implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadTime(s *smithy.Schema, v *time.Time) error { if d.inHeaderList { @@ -358,27 +248,6 @@ func (d *ShapeDeserializer) ReadTime(s *smithy.Schema, v *time.Time) error { return d.body.ReadTime(s, v) } -// ReadTimePtr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadTimePtr(s *smithy.Schema, v **time.Time) error { - if d.inHeaderList { - var val time.Time - if err := d.ReadTime(s, &val); err != nil { - return err - } - *v = &val - return nil - } - if d.inBindings { - t, err := d.readHeaderTime(s) - if err != nil { - return err - } - *v = &t - return nil - } - return d.body.ReadTimePtr(s, v) -} - func (d *ShapeDeserializer) readHeaderTime(s *smithy.Schema) (time.Time, error) { h, _ := isHTTPHeader(s) hv := d.response.Header.Get(h.Name) @@ -615,3 +484,13 @@ func readHeaderFloat[T floatn](d *ShapeDeserializer, s *smithy.Schema, v *T) err *v = T(n) return nil } + +// ReadBigInt is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigInt(_ *smithy.Schema, _ *big.Int) error { + return fmt.Errorf("unimplemented") +} + +// ReadBigFloat is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigFloat(_ *smithy.Schema, _ *big.Float) error { + return fmt.Errorf("unimplemented") +} diff --git a/aws-protocols/internal/json/shape_deserializer.go b/aws-protocols/internal/json/shape_deserializer.go index b008041e6..ac1686e14 100644 --- a/aws-protocols/internal/json/shape_deserializer.go +++ b/aws-protocols/internal/json/shape_deserializer.go @@ -4,6 +4,7 @@ import ( "encoding/base64" "fmt" "math" + "math/big" "strconv" "strings" "time" @@ -120,26 +121,6 @@ func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { return err } -// ReadInt8Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt8Ptr(s *smithy.Schema, v **int8) error { - return readPtr(d, s, v, d.ReadInt8) -} - -// ReadInt16Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt16Ptr(s *smithy.Schema, v **int16) error { - return readPtr(d, s, v, d.ReadInt16) -} - -// ReadInt32Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt32Ptr(s *smithy.Schema, v **int32) error { - return readPtr(d, s, v, d.ReadInt32) -} - -// ReadInt64Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt64Ptr(s *smithy.Schema, v **int64) error { - return readPtr(d, s, v, d.ReadInt64) -} - func (d *ShapeDeserializer) readInt(min, max int64) (int64, error) { tok, err := d.next() if err != nil { @@ -176,16 +157,6 @@ func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { return err } -// ReadFloat32Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadFloat32Ptr(s *smithy.Schema, v **float32) error { - return readPtr(d, s, v, d.ReadFloat32) -} - -// ReadFloat64Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadFloat64Ptr(s *smithy.Schema, v **float64) error { - return readPtr(d, s, v, d.ReadFloat64) -} - func (d *ShapeDeserializer) readFloat() (float64, error) { tok, err := d.next() if err != nil { @@ -231,11 +202,6 @@ func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { } } -// ReadBoolPtr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadBoolPtr(s *smithy.Schema, v **bool) error { - return readPtr(d, s, v, d.ReadBool) -} - // ReadString implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { tok, err := d.next() @@ -259,11 +225,6 @@ func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { return nil } -// ReadStringPtr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadStringPtr(s *smithy.Schema, v **string) error { - return readPtr(d, s, v, d.ReadString) -} - // ReadTime implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadTime(schema *smithy.Schema, v *time.Time) error { format := "epoch-seconds" @@ -306,11 +267,6 @@ func (d *ShapeDeserializer) ReadTime(schema *smithy.Schema, v *time.Time) error } } -// ReadTimePtr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadTimePtr(schema *smithy.Schema, v **time.Time) error { - return readPtr(d, schema, v, d.ReadTime) -} - // ReadBlob implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { if isNil, err := d.ReadNil(s); isNil || err != nil { @@ -455,6 +411,12 @@ func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { return d.ReadStructMember() } + if isNil, err := d.ReadNil(member); err != nil { + return nil, err + } else if isNil { + return d.ReadStructMember() + } + return member, nil } @@ -532,17 +494,6 @@ func (d *ShapeDeserializer) ReadDocument(schema *smithy.Schema, v *document.Valu return nil } -func readPtr[T any](d *ShapeDeserializer, s *smithy.Schema, v **T, read func(*smithy.Schema, *T) error) error { - if isNil, err := d.ReadNil(s); isNil || err != nil { - return err - } - - if *v == nil { - *v = new(T) - } - return read(s, *v) -} - func unquote(tok []byte) (string, error) { if s, ok := stdlib.UnquoteBytes(tok); ok { return string(s), nil @@ -575,3 +526,13 @@ func isLCB(tok []byte) bool { return tok[0] == '{' } func isRCB(tok []byte) bool { return tok[0] == '}' } func isLSB(tok []byte) bool { return tok[0] == '[' } func isRSB(tok []byte) bool { return tok[0] == ']' } + +// ReadBigInt is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigInt(_ *smithy.Schema, _ *big.Int) error { + return fmt.Errorf("unimplemented") +} + +// ReadBigFloat is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigFloat(_ *smithy.Schema, _ *big.Float) error { + return fmt.Errorf("unimplemented") +} diff --git a/aws-protocols/internal/xml/shape_deserializer.go b/aws-protocols/internal/xml/shape_deserializer.go index c760aac03..4717a8b18 100644 --- a/aws-protocols/internal/xml/shape_deserializer.go +++ b/aws-protocols/internal/xml/shape_deserializer.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/xml" "fmt" + "math/big" "math" "strconv" "strings" @@ -417,11 +418,6 @@ func (d *ShapeDeserializer) ReadBool(_ *smithy.Schema, v *bool) error { return nil } -// ReadBoolPtr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadBoolPtr(s *smithy.Schema, v **bool) error { - return readPtr(d, s, v, d.ReadBool) -} - // ReadInt8 implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadInt8(s *smithy.Schema, v *int8) error { n, err := d.readInt(8) @@ -466,26 +462,6 @@ func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { return nil } -// ReadInt8Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt8Ptr(s *smithy.Schema, v **int8) error { - return readPtr(d, s, v, d.ReadInt8) -} - -// ReadInt16Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt16Ptr(s *smithy.Schema, v **int16) error { - return readPtr(d, s, v, d.ReadInt16) -} - -// ReadInt32Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt32Ptr(s *smithy.Schema, v **int32) error { - return readPtr(d, s, v, d.ReadInt32) -} - -// ReadInt64Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt64Ptr(s *smithy.Schema, v **int64) error { - return readPtr(d, s, v, d.ReadInt64) -} - // ReadFloat32 implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error { n, err := d.readFloat() @@ -508,16 +484,6 @@ func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { return nil } -// ReadFloat32Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadFloat32Ptr(s *smithy.Schema, v **float32) error { - return readPtr(d, s, v, d.ReadFloat32) -} - -// ReadFloat64Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadFloat64Ptr(s *smithy.Schema, v **float64) error { - return readPtr(d, s, v, d.ReadFloat64) -} - // ReadString implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadString(_ *smithy.Schema, v *string) error { text, err := d.chardata() @@ -529,11 +495,6 @@ func (d *ShapeDeserializer) ReadString(_ *smithy.Schema, v *string) error { return nil } -// ReadStringPtr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadStringPtr(s *smithy.Schema, v **string) error { - return readPtr(d, s, v, d.ReadString) -} - // ReadTime implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadTime(schema *smithy.Schema, v *time.Time) error { format := "date-time" @@ -572,11 +533,6 @@ func (d *ShapeDeserializer) ReadTime(schema *smithy.Schema, v *time.Time) error return nil } -// ReadTimePtr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadTimePtr(schema *smithy.Schema, v **time.Time) error { - return readPtr(d, schema, v, d.ReadTime) -} - // ReadBlob implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { text, err := d.chardata() @@ -628,13 +584,6 @@ func (d *ShapeDeserializer) ReadDocument(_ *smithy.Schema, _ *document.Value) er return fmt.Errorf("Document not supported") } -func readPtr[T any](d *ShapeDeserializer, s *smithy.Schema, v **T, read func(*smithy.Schema, *T) error) error { - if *v == nil { - *v = new(T) - } - return read(s, *v) -} - func (d *ShapeDeserializer) token() (xml.Token, error) { if d.peeked != nil { tok := d.peeked @@ -770,3 +719,13 @@ func stripPrefix(name string) string { } return name } + +// ReadBigInt is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigInt(_ *smithy.Schema, _ *big.Int) error { + return fmt.Errorf("unimplemented") +} + +// ReadBigFloat is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigFloat(_ *smithy.Schema, _ *big.Float) error { + return fmt.Errorf("unimplemented") +} diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java index 02c763f39..766685cb4 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureDeserializer.java @@ -57,21 +57,21 @@ public void accept(GoWriter writer) { private void renderMember(GoWriter writer, MemberShape member, Shape target, String ident) { var schemaName = SchemaGenerator.getMemberSchemaRef(shape, member, ctx.service()); - var ptrSuffix = nilIndex.isNillable(member) ? "Ptr" : ""; + var isNillable = nilIndex.isNillable(member); switch (target.getType()) { case BYTE -> - writer.write("return d.ReadInt8$L($L, &$L)", ptrSuffix, schemaName, ident); + writeReadScalar(writer, isNillable, ident, schemaName, "ReadInt8", "int8"); case SHORT -> - writer.write("return d.ReadInt16$L($L, &$L)", ptrSuffix, schemaName, ident); + writeReadScalar(writer, isNillable, ident, schemaName, "ReadInt16", "int16"); case INTEGER -> - writer.write("return d.ReadInt32$L($L, &$L)", ptrSuffix, schemaName, ident); + writeReadScalar(writer, isNillable, ident, schemaName, "ReadInt32", "int32"); case LONG -> - writer.write("return d.ReadInt64$L($L, &$L)", ptrSuffix, schemaName, ident); + writeReadScalar(writer, isNillable, ident, schemaName, "ReadInt64", "int64"); case FLOAT -> - writer.write("return d.ReadFloat32$L($L, &$L)", ptrSuffix, schemaName, ident); + writeReadScalar(writer, isNillable, ident, schemaName, "ReadFloat32", "float32"); case DOUBLE -> - writer.write("return d.ReadFloat64$L($L, &$L)", ptrSuffix, schemaName, ident); + writeReadScalar(writer, isNillable, ident, schemaName, "ReadFloat64", "float64"); case STRING, ENUM -> { if (ShapeUtil.isEnum(target)) { @@ -83,7 +83,7 @@ private void renderMember(GoWriter writer, MemberShape member, Shape target, Str $2L = $3T(ev) return nil""", schemaName, ident, ctx.symbolProvider().toSymbol(target)); } else { - writer.write("return d.ReadString$L($L, &$L)", ptrSuffix, schemaName, ident); + writeReadScalar(writer, isNillable, ident, schemaName, "ReadString", "string"); } } case INT_ENUM -> @@ -95,9 +95,9 @@ private void renderMember(GoWriter writer, MemberShape member, Shape target, Str $2L = $3T(ev) return nil""", schemaName, ident, ctx.symbolProvider().toSymbol(target)); case BOOLEAN -> - writer.write("return d.ReadBool$L($L, &$L)", ptrSuffix, schemaName, ident); + writeReadScalar(writer, isNillable, ident, schemaName, "ReadBool", "bool"); case TIMESTAMP -> - writer.write("return d.ReadTime$L($L, &$L)", ptrSuffix, schemaName, ident); + writeReadScalar(writer, isNillable, ident, schemaName, "ReadTime", "time.Time"); case BLOB -> writer.write("return d.ReadBlob($L, &$L)", schemaName, ident); @@ -134,4 +134,13 @@ private void renderMember(GoWriter writer, MemberShape member, Shape target, Str case MEMBER, SERVICE, RESOURCE, OPERATION -> throw new UnsupportedShapeException(target.getType()); } } + + private void writeReadScalar(GoWriter writer, boolean isNillable, String ident, String schemaName, + String readMethod, String goType) { + if (isNillable) { + writer.write("$1L = new($4L)\nreturn d.$3L($2L, $1L)", ident, schemaName, readMethod, goType); + } else { + writer.write("return d.$3L($2L, &$1L)", ident, schemaName, readMethod); + } + } } diff --git a/eventstream/deserializer.go b/eventstream/deserializer.go index 81f983c74..7cd0039b7 100644 --- a/eventstream/deserializer.go +++ b/eventstream/deserializer.go @@ -2,6 +2,7 @@ package eventstream import ( "fmt" + "math/big" "time" "github.com/aws/smithy-go" @@ -115,18 +116,6 @@ func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { return d.inner.ReadString(s, v) } -func (d *ShapeDeserializer) ReadStringPtr(s *smithy.Schema, v **string) error { - if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { - return nil - } - var val string - if err := d.ReadString(s, &val); err != nil { - return err - } - *v = &val - return nil -} - func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { if d.inBindings && isEventHeader(s) { hv := d.Message.Headers.Get(s.MemberName()) @@ -143,18 +132,6 @@ func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { return d.inner.ReadBool(s, v) } -func (d *ShapeDeserializer) ReadBoolPtr(s *smithy.Schema, v **bool) error { - if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { - return nil - } - var val bool - if err := d.ReadBool(s, &val); err != nil { - return err - } - *v = &val - return nil -} - func (d *ShapeDeserializer) readHeaderInt64(name string) (int64, bool, error) { hv := d.Message.Headers.Get(name) if hv == nil { @@ -194,18 +171,6 @@ func (d *ShapeDeserializer) ReadInt8(s *smithy.Schema, v *int8) error { return d.inner.ReadInt8(s, v) } -func (d *ShapeDeserializer) ReadInt8Ptr(s *smithy.Schema, v **int8) error { - if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { - return nil - } - var val int8 - if err := d.ReadInt8(s, &val); err != nil { - return err - } - *v = &val - return nil -} - func (d *ShapeDeserializer) ReadInt16(s *smithy.Schema, v *int16) error { if d.inBindings && isEventHeader(s) { return readEventHeaderInt(d, s, v) @@ -213,18 +178,6 @@ func (d *ShapeDeserializer) ReadInt16(s *smithy.Schema, v *int16) error { return d.inner.ReadInt16(s, v) } -func (d *ShapeDeserializer) ReadInt16Ptr(s *smithy.Schema, v **int16) error { - if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { - return nil - } - var val int16 - if err := d.ReadInt16(s, &val); err != nil { - return err - } - *v = &val - return nil -} - func (d *ShapeDeserializer) ReadInt32(s *smithy.Schema, v *int32) error { if d.inBindings && isEventHeader(s) { return readEventHeaderInt(d, s, v) @@ -232,18 +185,6 @@ func (d *ShapeDeserializer) ReadInt32(s *smithy.Schema, v *int32) error { return d.inner.ReadInt32(s, v) } -func (d *ShapeDeserializer) ReadInt32Ptr(s *smithy.Schema, v **int32) error { - if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { - return nil - } - var val int32 - if err := d.ReadInt32(s, &val); err != nil { - return err - } - *v = &val - return nil -} - func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { if d.inBindings && isEventHeader(s) { return readEventHeaderInt(d, s, v) @@ -251,34 +192,14 @@ func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { return d.inner.ReadInt64(s, v) } -func (d *ShapeDeserializer) ReadInt64Ptr(s *smithy.Schema, v **int64) error { - if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { - return nil - } - var val int64 - if err := d.ReadInt64(s, &val); err != nil { - return err - } - *v = &val - return nil -} - func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error { return d.inner.ReadFloat32(s, v) } -func (d *ShapeDeserializer) ReadFloat32Ptr(s *smithy.Schema, v **float32) error { - return d.inner.ReadFloat32Ptr(s, v) -} - func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { return d.inner.ReadFloat64(s, v) } -func (d *ShapeDeserializer) ReadFloat64Ptr(s *smithy.Schema, v **float64) error { - return d.inner.ReadFloat64Ptr(s, v) -} - func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { if d.inBindings { if isEventHeader(s) { @@ -317,18 +238,6 @@ func (d *ShapeDeserializer) ReadTime(s *smithy.Schema, v *time.Time) error { return d.inner.ReadTime(s, v) } -func (d *ShapeDeserializer) ReadTimePtr(s *smithy.Schema, v **time.Time) error { - if d.inBindings && isEventHeader(s) && d.Message.Headers.Get(s.MemberName()) == nil { - return nil - } - var val time.Time - if err := d.ReadTime(s, &val); err != nil { - return err - } - *v = &val - return nil -} - func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { return d.inner.ReadList(s) } @@ -362,3 +271,13 @@ func isEventBound(schema *smithy.Schema) bool { _, p := smithy.SchemaTrait[*traits.EventPayload](schema) return h || p } + +// ReadBigInt is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigInt(_ *smithy.Schema, _ *big.Int) error { + return fmt.Errorf("unimplemented") +} + +// ReadBigFloat is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigFloat(_ *smithy.Schema, _ *big.Float) error { + return fmt.Errorf("unimplemented") +} diff --git a/serde.go b/serde.go index 418ff84cb..6debe1280 100644 --- a/serde.go +++ b/serde.go @@ -23,7 +23,13 @@ import ( // } // // func (v *PutItemInput) SerializeMembers(s smithy.ShapeSerializer) { -// // TODO(serde2) +// if v.TableName != nil { +// s.WriteString(schemas.PutItemInput_TableName, *v.TableName) +// } +// if v.Item != nil { +// serializeAttributeMap(s, schemas.PutItemInput_Item, v.Item) +// } +// // ... // } type ShapeSerializer interface { Bytes() []byte @@ -64,54 +70,27 @@ type ShapeDeserializer interface { ReadInt16(*Schema, *int16) error ReadInt32(*Schema, *int32) error ReadInt64(*Schema, *int64) error - - ReadInt8Ptr(*Schema, **int8) error - ReadInt16Ptr(*Schema, **int16) error - ReadInt32Ptr(*Schema, **int32) error - ReadInt64Ptr(*Schema, **int64) error - ReadFloat32(*Schema, *float32) error ReadFloat64(*Schema, *float64) error - - ReadFloat32Ptr(*Schema, **float32) error - ReadFloat64Ptr(*Schema, **float64) error - ReadBool(*Schema, *bool) error - ReadBoolPtr(*Schema, **bool) error - ReadString(*Schema, *string) error - ReadStringPtr(*Schema, **string) error - - ReadTime(*Schema, *time.Time) error - ReadTimePtr(*Schema, **time.Time) error - ReadBlob(*Schema, *[]byte) error - - ReadList(*Schema) error - // returns true if there's another item in the list, false at the end and - // an error if a decode error is encountered. use other deserializer - // methods to read the expected type from the deserializer - ReadListItem(*Schema) (hasMoreElements bool, err error) - - ReadMap(*Schema) error - // the bool will be true if there's another key in the list and the string - // will have the value of that key, with any decode error in the error. use - // other deserializer methods to read the expected type. - ReadMapKey(*Schema) (key string, hasMoreElements bool, err error) + ReadTime(*Schema, *time.Time) error + ReadBigInt(*Schema, *big.Int) error + ReadBigFloat(*Schema, *big.Float) error + ReadNil(*Schema) (bool, error) ReadStruct(*Schema) error - // returns the member schema for the given struct, nil when there are no - // more members, with any decode error in the error. use other deserializer - // methods to read the expected type. ReadStructMember() (*Schema, error) - // returns the schema for the variant that the union is ReadUnion(*Schema) (*Schema, error) + ReadDocument(*Schema, *document.Value) error - // returns true if the next value is null (and consumes it) - ReadNil(*Schema) (bool, error) + ReadList(*Schema) error + ReadListItem(*Schema) (hasMoreElements bool, err error) - ReadDocument(*Schema, *document.Value) error + ReadMap(*Schema) error + ReadMapKey(*Schema) (key string, hasMoreElements bool, err error) } // Serializable is an entity that can describe itself to a ShapeSerializer to diff --git a/smithy-http-protocols/internal/cbor/shape_deserializer.go b/smithy-http-protocols/internal/cbor/shape_deserializer.go index 4a4a49382..eac54c112 100644 --- a/smithy-http-protocols/internal/cbor/shape_deserializer.go +++ b/smithy-http-protocols/internal/cbor/shape_deserializer.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "math" + "math/big" "time" "github.com/aws/smithy-go" @@ -218,26 +219,6 @@ func (d *ShapeDeserializer) ReadInt64(s *smithy.Schema, v *int64) error { return readInt(d, v, math.MinInt64, math.MaxInt64) } -// ReadInt8Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt8Ptr(s *smithy.Schema, v **int8) error { - return readPtr(d, s, v, d.ReadInt8) -} - -// ReadInt16Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt16Ptr(s *smithy.Schema, v **int16) error { - return readPtr(d, s, v, d.ReadInt16) -} - -// ReadInt32Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt32Ptr(s *smithy.Schema, v **int32) error { - return readPtr(d, s, v, d.ReadInt32) -} - -// ReadInt64Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadInt64Ptr(s *smithy.Schema, v **int64) error { - return readPtr(d, s, v, d.ReadInt64) -} - // ReadFloat32 implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadFloat32(s *smithy.Schema, v *float32) error { return readFloat(d, v) @@ -248,16 +229,6 @@ func (d *ShapeDeserializer) ReadFloat64(s *smithy.Schema, v *float64) error { return readFloat(d, v) } -// ReadFloat32Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadFloat32Ptr(s *smithy.Schema, v **float32) error { - return readPtr(d, s, v, d.ReadFloat32) -} - -// ReadFloat64Ptr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadFloat64Ptr(s *smithy.Schema, v **float64) error { - return readPtr(d, s, v, d.ReadFloat64) -} - // ReadBool implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { if d.eof() { @@ -281,11 +252,6 @@ func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { } } -// ReadBoolPtr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadBoolPtr(s *smithy.Schema, v **bool) error { - return readPtr(d, s, v, d.ReadBool) -} - // ReadString implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { if d.eof() { @@ -330,11 +296,6 @@ func (d *ShapeDeserializer) ReadString(s *smithy.Schema, v *string) error { return nil } -// ReadStringPtr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadStringPtr(s *smithy.Schema, v **string) error { - return readPtr(d, s, v, d.ReadString) -} - // ReadTime implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadTime(schema *smithy.Schema, v *time.Time) error { if d.eof() { @@ -377,11 +338,6 @@ func (d *ShapeDeserializer) ReadTime(schema *smithy.Schema, v *time.Time) error } } -// ReadTimePtr implements [smithy.ShapeDeserializer]. -func (d *ShapeDeserializer) ReadTimePtr(schema *smithy.Schema, v **time.Time) error { - return readPtr(d, schema, v, d.ReadTime) -} - // ReadBlob implements [smithy.ShapeDeserializer]. func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { if isNil, err := d.ReadNil(s); isNil || err != nil { @@ -555,6 +511,12 @@ func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { return d.ReadStructMember() } + if isNil, err := d.ReadNil(member); err != nil { + return nil, err + } else if isNil { + return d.ReadStructMember() + } + return member, nil } @@ -701,16 +663,6 @@ func (d *ShapeDeserializer) skipIndefiniteBytes() error { return nil } -func readPtr[T any](d *ShapeDeserializer, s *smithy.Schema, v **T, read func(*smithy.Schema, *T) error) error { - if isNil, err := d.ReadNil(s); isNil || err != nil { - return err - } - if *v == nil { - *v = new(T) - } - return read(s, *v) -} - type tint interface { int8 | int16 | int32 | int64 } @@ -765,3 +717,13 @@ func GetProtocolErrorInfo(p []byte) (typ, message string, err error) { return typ, message, nil } + +// ReadBigInt is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigInt(_ *smithy.Schema, _ *big.Int) error { + return fmt.Errorf("unimplemented") +} + +// ReadBigFloat is unimplemented and will return an error. +func (d *ShapeDeserializer) ReadBigFloat(_ *smithy.Schema, _ *big.Float) error { + return fmt.Errorf("unimplemented") +} From 30af9be2d506d43b893d5bc492668da2aac66d58 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Fri, 22 May 2026 11:55:14 -0400 Subject: [PATCH 24/38] fix benchmark test --- aws-protocols/internal/json/benchmark_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aws-protocols/internal/json/benchmark_test.go b/aws-protocols/internal/json/benchmark_test.go index 92dd355b2..ac40e383e 100644 --- a/aws-protocols/internal/json/benchmark_test.go +++ b/aws-protocols/internal/json/benchmark_test.go @@ -490,7 +490,8 @@ func newDeserializeConsumedCapacity(d smithy.ShapeDeserializer, v *ConsumedCapac return smithy.ReadStruct(d, schemaConsumedCapacity, func(ms *smithy.Schema) error { switch ms { case schemaConsumedCapacity_TableName: - return d.ReadStringPtr(schemaConsumedCapacity_TableName, &v.TableName) + v.TableName = new(string) + return d.ReadString(schemaConsumedCapacity_TableName, v.TableName) case schemaConsumedCapacity_CapacityUnits: return d.ReadFloat64(schemaConsumedCapacity_CapacityUnits, &v.CapacityUnits) } From 71e665681fff6e8a0d0cbe9fc1d527a68b6e78ef Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Fri, 22 May 2026 11:58:17 -0400 Subject: [PATCH 25/38] useExperimentalSerde -> useLegacySerde --- .../smithy/go/codegen/ClientOptions.java | 8 ++++---- .../smithy/go/codegen/CodegenVisitor.java | 18 +++++++++--------- .../go/codegen/EventStreamGenerator.java | 2 +- .../amazon/smithy/go/codegen/GoSettings.java | 16 ++++++++-------- .../smithy/go/codegen/OperationGenerator.java | 2 +- .../go/codegen/ProtocolDocumentGenerator.java | 2 +- .../smithy/go/codegen/ServiceGenerator.java | 2 +- .../smithy/go/codegen/StructureGenerator.java | 8 ++++---- .../smithy/go/codegen/UnionGenerator.java | 2 +- .../amazon/smithy/go/codegen/TestUtils.java | 1 + 10 files changed, 31 insertions(+), 30 deletions(-) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ClientOptions.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ClientOptions.java index 3dc963102..42092aaed 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ClientOptions.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ClientOptions.java @@ -85,7 +85,7 @@ private Writable generate() { $fields:W - $experimentalSerdeProtocolFields:W + $schemaSerdeProtocolFields:W $protocolFields:W } @@ -106,13 +106,13 @@ private Writable generate() { "copy", generateCopy(), "getIdentityResolver", generateGetIdentityResolver(), "helpers", generateHelpers(), - "experimentalSerdeProtocolFields", ctx.settings().useExperimentalSerde() - ? generateExperimentalSerdeProtocolFields() + "schemaSerdeProtocolFields", !ctx.settings().useLegacySerde() + ? generateSchemaSerdeProtocolFields() : emptyGoTemplate() )); } - private Writable generateExperimentalSerdeProtocolFields() { + private Writable generateSchemaSerdeProtocolFields() { ensureSupportedProtocol(); return goTemplate(""" $D diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java index be68cde2c..e3b89bdea 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java @@ -242,7 +242,7 @@ void execute() { // // There is a block further down after the serde2 stuff that also uses the protocol generator, but that all will // have to be pulled out of ProtocolGenerator and just be generic. - if (!settings.useExperimentalSerde() && protocolGenerator != null) { + if (settings.useLegacySerde() && protocolGenerator != null) { LOGGER.info("Generating serde for protocol " + protocolGenerator.getProtocol() + " on " + service.getId()); ProtocolGenerator.GenerationContext.Builder contextBuilder = ProtocolGenerator.GenerationContext.builder() .protocolName(protocolGenerator.getProtocolName()) @@ -280,7 +280,7 @@ void execute() { LOGGER.info("Generating protocol tests for " + service.getId()); ProtocolUtils.generateHttpProtocolTests(ctx); - if (settings.useExperimentalSerde()) { + if (!settings.useLegacySerde()) { protocolDocumentGenerator.generateLegacyInternalDocumentTypes(ctx); var shapes = new ArrayList<>(ctx.serdeShapes()); @@ -420,7 +420,7 @@ void execute() { @Override protected Void getDefault(Shape shape) { - if (settings.useExperimentalSerde() + if (!settings.useLegacySerde() && !shape.isMemberShape() && !shape.isOperationShape() && !shape.isServiceShape() @@ -437,7 +437,7 @@ public Void structureShape(StructureShape shape) { if (shape.getId().getNamespace().equals(CodegenUtils.getSyntheticTypeNamespace())) { // For synthetic clones with an archetype, generate the schema under // the archetype's name. Stub synthetics (no archetype) are skipped. - if (settings.useExperimentalSerde() && !CodegenUtils.isStubSynthetic(shape)) { + if (!settings.useLegacySerde() && !CodegenUtils.isStubSynthetic(shape)) { var archetypeId = shape.getTrait(Synthetic.class).get().getArchetype().get(); if (emittedSchemas.add(archetypeId)) { var archetype = model.expectShape(archetypeId, StructureShape.class); @@ -450,7 +450,7 @@ public Void structureShape(StructureShape shape) { writers.useShapeWriter(shape, writer -> new StructureGenerator(ctx, writer, shape, protocolGenerator).run()); - if (settings.useExperimentalSerde() && emittedSchemas.add(shape.getId())) { + if (!settings.useLegacySerde() && emittedSchemas.add(shape.getId())) { ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", writer -> new SchemaGenerator(ctx, shape).accept(writer)); } @@ -463,7 +463,7 @@ public Void stringShape(StringShape shape) { if (shape.hasTrait(EnumTrait.class)) { writers.useShapeWriter(shape, writer -> new EnumGenerator(symbolProvider, writer, shape).run()); } - if (settings.useExperimentalSerde() && emittedSchemas.add(shape.getId())) { + if (!settings.useLegacySerde() && emittedSchemas.add(shape.getId())) { ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", writer -> new SchemaGenerator(ctx, shape).accept(writer)); } @@ -476,7 +476,7 @@ public Void unionShape(UnionShape shape) { writers.useShapeWriter(shape, generator::generateUnion); writers.useShapeExportedTestWriter(shape, generator::generateUnionExamples); - if (settings.useExperimentalSerde() && emittedSchemas.add(shape.getId())) { + if (!settings.useLegacySerde() && emittedSchemas.add(shape.getId())) { ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", writer -> new SchemaGenerator(ctx, shape).accept(writer)); } @@ -530,7 +530,7 @@ public Void serviceShape(ServiceShape shape) { } }); - if (ctx.settings().useExperimentalSerde()) { + if (!ctx.settings().useLegacySerde()) { writers.useShapeWriter(shape, new Serde2SerializeRequestMiddleware()); writers.useShapeWriter(shape, new Serde2DeserializeResponseMiddleware()); } @@ -543,7 +543,7 @@ public Void serviceShape(ServiceShape shape) { @Override public Void intEnumShape(IntEnumShape shape) { writers.useShapeWriter(shape, writer -> new IntEnumGenerator(symbolProvider, writer, shape).run()); - if (settings.useExperimentalSerde() && emittedSchemas.add(shape.getId())) { + if (!settings.useLegacySerde() && emittedSchemas.add(shape.getId())) { ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", writer -> new SchemaGenerator(ctx, shape).accept(writer)); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/EventStreamGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/EventStreamGenerator.java index f482942a7..4bfa4cb84 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/EventStreamGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/EventStreamGenerator.java @@ -140,7 +140,7 @@ public void generateEventStreamInterfaces() { }); }); - if (settings.useExperimentalSerde()) { + if (!settings.useLegacySerde()) { inputEvents.forEach(shapeId -> { var union = model.expectShape(shapeId, UnionShape.class); generateWriterAdapter(writer, union); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java index d68af2440..1b845bf8f 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/GoSettings.java @@ -55,7 +55,7 @@ public final class GoSettings { private static final String MODULE_DESCRIPTION = "moduleDescription"; private static final String MODULE_VERSION = "moduleVersion"; private static final String GENERATE_GO_MOD = "generateGoMod"; - private static final String USE_EXPERIMENTAL_SERDE = "useExperimentalSerde"; + private static final String USE_LEGACY_SERDE = "useLegacySerde"; private static final String GO_DIRECTIVE = "goDirective"; private ShapeId service; @@ -63,7 +63,7 @@ public final class GoSettings { private String moduleDescription = ""; private String moduleVersion; private Boolean generateGoMod = false; - private Boolean useExperimentalSerde = false; + private Boolean useLegacySerde = false; private String goDirective = GoModuleInfo.DEFAULT_GO_DIRECTIVE; private ShapeId protocol; private ArtifactType artifactType; @@ -88,7 +88,7 @@ public static GoSettings from(ObjectNode config) { public static GoSettings from(ObjectNode config, ArtifactType artifactType) { GoSettings settings = new GoSettings(); config.warnIfAdditionalProperties( - Arrays.asList(SERVICE, MODULE_NAME, MODULE_DESCRIPTION, MODULE_VERSION, GENERATE_GO_MOD, USE_EXPERIMENTAL_SERDE, GO_DIRECTIVE)); + Arrays.asList(SERVICE, MODULE_NAME, MODULE_DESCRIPTION, MODULE_VERSION, GENERATE_GO_MOD, USE_LEGACY_SERDE, GO_DIRECTIVE)); settings.setArtifactType(artifactType); settings.setService(config.expectStringMember(SERVICE).expectShapeId()); settings.setModuleName(config.expectStringMember(MODULE_NAME).getValue()); @@ -96,7 +96,7 @@ public static GoSettings from(ObjectNode config, ArtifactType artifactType) { MODULE_DESCRIPTION, settings.getModuleName() + " client")); settings.setModuleVersion(config.getStringMemberOrDefault(MODULE_VERSION, null)); settings.setGenerateGoMod(config.getBooleanMemberOrDefault(GENERATE_GO_MOD, false)); - settings.setUseExperimentalSerde(config.getBooleanMemberOrDefault(USE_EXPERIMENTAL_SERDE, false)); + settings.setUseLegacySerde(config.getBooleanMemberOrDefault(USE_LEGACY_SERDE, false)); settings.setGoDirective(config.getStringMemberOrDefault(GO_DIRECTIVE, GoModuleInfo.DEFAULT_GO_DIRECTIVE)); return settings; } @@ -220,12 +220,12 @@ public void setGenerateGoMod(Boolean generateGoMod) { this.generateGoMod = Objects.requireNonNull(generateGoMod); } - public Boolean useExperimentalSerde() { - return useExperimentalSerde; + public Boolean useLegacySerde() { + return useLegacySerde; } - public void setUseExperimentalSerde(Boolean value) { - this.useExperimentalSerde = Objects.requireNonNull(value); + public void setUseLegacySerde(Boolean value) { + this.useLegacySerde = Objects.requireNonNull(value); } /** diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java index 94d44c0df..f531bdc9e 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/OperationGenerator.java @@ -277,7 +277,7 @@ private void generateOperationProtocolMiddlewareAdders() { return err }"""); - if (!ctx.settings().useExperimentalSerde()) { + if (ctx.settings().useLegacySerde()) { // Add request serializer middleware String serializerMiddlewareName = ProtocolGenerator.getSerializeMiddlewareName( operation.getId(), service, protocolGenerator.getProtocolName()); diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java index 142d71c85..867a2d81e 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ProtocolDocumentGenerator.java @@ -420,7 +420,7 @@ private void writeInternalDocumentImplementation( }); writer.write(""); - if (settings.useExperimentalSerde()) { + if (!settings.useLegacySerde()) { writer.write("func (m $P) Value() any { return m.value }", typeSymbol); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java index c5e191e9b..07d6d0d6e 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/ServiceGenerator.java @@ -257,7 +257,7 @@ func New(options $options:L, optFns ...func(*$options:L)) *$client:L { .map(this::generateClientMemberResolver) .toList() ).compose(), - "experimentalSerdeResolvers", settings.useExperimentalSerde()? generateExperimentalSerdeResolvers() : emptyGoTemplate() + "experimentalSerdeResolvers", !settings.useLegacySerde()? generateExperimentalSerdeResolvers() : emptyGoTemplate() )); } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java index b5924ac76..d5035b9d5 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/StructureGenerator.java @@ -56,7 +56,7 @@ public final class StructureGenerator implements Runnable { private Symbol symbol; private final ServiceShape service; private final ProtocolGenerator protocolGenerator; - private final boolean useExperimentalSerde; + private final boolean useLegacySerde; private final GoCodegenContext ctx; public StructureGenerator( @@ -73,7 +73,7 @@ public StructureGenerator( this.shape = shape; this.symbol = ctx.symbolProvider().toSymbol(shape); this.protocolGenerator = protocolGenerator; - this.useExperimentalSerde = ctx.settings().useExperimentalSerde(); + this.useLegacySerde = ctx.settings().useLegacySerde(); } public StructureGenerator( @@ -152,7 +152,7 @@ public void renderStructure(Runnable runnable, boolean isInputStructure) { // Only generate serde methods when the symbol matches the shape's natural symbol. // When a symbol override is provided (e.g. for synthetic shapes like event stream // initial reply structs), skip serde to avoid duplicate method declarations. - if (useExperimentalSerde && symbol.getName().equals(ctx.symbolProvider().toSymbol(shape).getName())) { + if (!useLegacySerde && symbol.getName().equals(ctx.symbolProvider().toSymbol(shape).getName())) { if (shape.hasTrait(InputTrait.class)) { writer.write(new StructureSerializer(ctx, shape)); } else if (shape.hasTrait(OutputTrait.class)) { @@ -246,7 +246,7 @@ private void renderErrorStructure() { } writer.write("func (e *$L) ErrorFault() smithy.ErrorFault { return $L }", structureSymbol.getName(), fault); - if (useExperimentalSerde) { + if (!useLegacySerde) { writer.write(new StructureDeserializer(ctx, shape)); } } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java index b891bd819..a5b480c04 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/UnionGenerator.java @@ -104,7 +104,7 @@ public void generateUnion(GoWriter writer) { writer.write("func (*$L) is$L() {}", exportedMemberName, symbol.getName()); - if (ctx.settings().useExperimentalSerde()) { + if (!ctx.settings().useLegacySerde()) { generateMemberSerializer(writer, member, exportedMemberName, target); generateMemberDeserializer(writer, member, exportedMemberName, target); } diff --git a/codegen/smithy-go-codegen/src/test/java/software/amazon/smithy/go/codegen/TestUtils.java b/codegen/smithy-go-codegen/src/test/java/software/amazon/smithy/go/codegen/TestUtils.java index 41e32074c..3b1d2fdb4 100644 --- a/codegen/smithy-go-codegen/src/test/java/software/amazon/smithy/go/codegen/TestUtils.java +++ b/codegen/smithy-go-codegen/src/test/java/software/amazon/smithy/go/codegen/TestUtils.java @@ -109,6 +109,7 @@ public static ObjectNode getSettingsNode( .withMember("generateGoMod", Node.from(generateGoMod)) .withMember("homepage", Node.from("https://docs.amplify.aws/")) .withMember("sdkId", Node.from(sdkId)) + .withMember("useLegacySerde", Node.from(true)) .withMember("author", Node.from("Amazon Web Services")) .withMember("gitRepo", Node.from("https://github.com/aws-amplify/amplify-codegen.git")) .withMember("swiftVersion", Node.from("5.5.0")) From 7cf6966504766189ca372fbbdedd493c74493561 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Fri, 22 May 2026 14:56:50 -0400 Subject: [PATCH 26/38] fixup protocol test --- aws-protocols/awsjson/awsjson.go | 13 ++++ aws-protocols/awsquery/awsquery.go | 7 +- aws-protocols/ec2query/ec2query.go | 4 +- .../internal/json/shape_deserializer.go | 6 +- aws-protocols/restjson1/restjson1.go | 8 +- aws-protocols/restxml/restxml.go | 8 +- .../smithy/go/codegen/CodegenVisitor.java | 74 ++++++------------- .../smithy/go/codegen/TypeRegistry.java | 4 +- .../go/codegen/serde2/ListDeserializer.java | 2 + .../go/codegen/serde2/MapDeserializer.java | 5 ++ .../codegen/serde2/StructureSerializer.java | 2 +- smithy-http-protocols/rpcv2/rpcv2.go | 4 + 12 files changed, 80 insertions(+), 57 deletions(-) diff --git a/aws-protocols/awsjson/awsjson.go b/aws-protocols/awsjson/awsjson.go index 8021df40e..a23f75303 100644 --- a/aws-protocols/awsjson/awsjson.go +++ b/aws-protocols/awsjson/awsjson.go @@ -97,6 +97,15 @@ func (p *Protocol) SerializeRequest( req.Header.Set("X-Amzn-Query-Mode", "true") } + if schema.Input == nil { + sreq, err := req.SetStream(bytes.NewReader([]byte("{}"))) + if err != nil { + return fmt.Errorf("set stream: %w", err) + } + *req = *sreq + return nil + } + ss := internaljson.NewShapeSerializer() in.Serialize(ss) @@ -125,6 +134,10 @@ func (p *Protocol) DeserializeResponse( return nil } + if schema.Output == nil { + return nil + } + payload, err := io.ReadAll(resp.Body) if err != nil { return &smithy.DeserializationError{Err: err} diff --git a/aws-protocols/awsquery/awsquery.go b/aws-protocols/awsquery/awsquery.go index dabc08e30..bf126ad1d 100644 --- a/aws-protocols/awsquery/awsquery.go +++ b/aws-protocols/awsquery/awsquery.go @@ -54,7 +54,9 @@ func (p *Protocol) SerializeRequest( req.Header.Set("Content-Type", "application/x-www-form-urlencoded") ss := internalquery.NewShapeSerializer(middleware.GetOperationName(ctx), p.version) - in.Serialize(ss) + if schema.Input != nil { + in.Serialize(ss) + } sreq, err := req.SetStream(bytes.NewReader(ss.Bytes())) if err != nil { @@ -88,6 +90,9 @@ func (p *Protocol) DeserializeResponse( inner, err := internalxml.ExtractElement(payload, middleware.GetOperationName(ctx)+"Result") if err != nil { + if schema.Output == nil { + return nil + } return &smithy.DeserializationError{Err: err} } diff --git a/aws-protocols/ec2query/ec2query.go b/aws-protocols/ec2query/ec2query.go index 052d0a93d..efdc6dd24 100644 --- a/aws-protocols/ec2query/ec2query.go +++ b/aws-protocols/ec2query/ec2query.go @@ -55,7 +55,9 @@ func (p *Protocol) SerializeRequest( ss := internalquery.NewShapeSerializer(middleware.GetOperationName(ctx), p.version, func(o *internalquery.ShapeSerializerOptions) { o.EC2Mode = true }, ) - in.Serialize(ss) + if schema.Input != nil { + in.Serialize(ss) + } sreq, err := req.SetStream(bytes.NewReader(ss.Bytes())) if err != nil { diff --git a/aws-protocols/internal/json/shape_deserializer.go b/aws-protocols/internal/json/shape_deserializer.go index ac1686e14..a881d0093 100644 --- a/aws-protocols/internal/json/shape_deserializer.go +++ b/aws-protocols/internal/json/shape_deserializer.go @@ -486,7 +486,11 @@ func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) // ReadDocument reads a JSON value into a document Value. func (d *ShapeDeserializer) ReadDocument(schema *smithy.Schema, v *document.Value) error { - vv, err := d.p.Value() + tok, err := d.next() + if err != nil { + return err + } + vv, err := d.p.value(tok) if err != nil { return err } diff --git a/aws-protocols/restjson1/restjson1.go b/aws-protocols/restjson1/restjson1.go index 23e88182f..ada360a51 100644 --- a/aws-protocols/restjson1/restjson1.go +++ b/aws-protocols/restjson1/restjson1.go @@ -57,7 +57,9 @@ func (p *Protocol) SerializeRequest( return err } - in.Serialize(serializer) + if op.Input != nil { + in.Serialize(serializer) + } contentType := "application/json" if op.IsInputEventStream() { @@ -82,6 +84,10 @@ func (p *Protocol) DeserializeResponse( return p.deserializeError(types, resp) } + if op.Output == nil { + return nil + } + // @httpPayload + @streaming = do not touch the body at all, it's the // caller's problem if so, ok := out.(smithy.StreamingOutput); ok { diff --git a/aws-protocols/restxml/restxml.go b/aws-protocols/restxml/restxml.go index ed420d9e0..bc4133cc7 100644 --- a/aws-protocols/restxml/restxml.go +++ b/aws-protocols/restxml/restxml.go @@ -79,7 +79,9 @@ func (p *Protocol) SerializeRequest( return err } - in.Serialize(serializer) + if op.Input != nil { + in.Serialize(serializer) + } contentType := "application/xml" if op.IsInputEventStream() { @@ -104,6 +106,10 @@ func (p *Protocol) DeserializeResponse( return p.deserializeError(types, resp) } + if op.Output == nil { + return nil + } + if so, ok := out.(smithy.StreamingOutput); ok { so.SetPayloadStream(resp.Body) deser := internalhttpbinding.NewShapeDeserializer(resp.Response, deser(nil, op.Output), nil) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java index e3b89bdea..8b55de69d 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/CodegenVisitor.java @@ -94,7 +94,6 @@ final class CodegenVisitor extends ShapeVisitor.Default { private final ProtocolDocumentGenerator protocolDocumentGenerator; private final EventStreamGenerator eventStreamGenerator; private final GoCodegenContext ctx; - private final Set emittedSchemas = new HashSet<>(); CodegenVisitor(PluginContext context) { // Load all integrations. @@ -306,26 +305,38 @@ void execute() { service.getVersion()); writer.write(""); + // Generate all shape schemas upfront. For synthetic shapes + // (input/output), resolve to the archetype. + var resolved = new ArrayList(); + var seen = new HashSet(); + for (Shape s : shapes) { + if (CodegenUtils.isStubSynthetic(s)) { + continue; + } + Shape target = s; + ShapeId id = s.getId(); + if (id.getNamespace().equals(CodegenUtils.getSyntheticTypeNamespace())) { + id = s.getTrait(Synthetic.class).get().getArchetype().get(); + target = model.expectShape(id); + } + if (seen.add(id)) { + new SchemaGenerator(ctx, target).accept(writer); + resolved.add(target); + } + } + writer.write(""); writer.writeDocs("Initialize schema members after all schemas are declared to avoid initialization cycles"); writer.openBlock("func init() {", "}", () -> { - // Topologically sort shapes so that target schemas have their - // members initialized before they are used as targets in - // AddMember calls. This ensures MapKey(), MapValue(), and - // ListMember() are non-nil when the member schema is created. var shapeIds = new HashSet(); - for (Shape s : shapes) { - if (!s.getId().getNamespace().equals(CodegenUtils.getSyntheticTypeNamespace())) { - shapeIds.add(s.getId()); - } + for (Shape s : resolved) { + shapeIds.add(s.getId()); } var sorted = new ArrayList(); var visited = new HashSet(); - for (Shape s : shapes) { - if (shapeIds.contains(s.getId())) { - topoVisit(s, model, shapeIds, visited, sorted); - } + for (Shape s : resolved) { + topoVisit(s, model, shapeIds, visited, sorted); } for (Shape shape : sorted) { @@ -333,7 +344,6 @@ void execute() { } }); }); - var lists = ctx.serdeShapes(ListShape.class); var maps = ctx.serdeShapes(MapShape.class); var unionSerdes = ctx.serdeShapes(UnionShape.class).stream() @@ -420,41 +430,17 @@ void execute() { @Override protected Void getDefault(Shape shape) { - if (!settings.useLegacySerde() - && !shape.isMemberShape() - && !shape.isOperationShape() - && !shape.isServiceShape() - && !shape.getId().getNamespace().equals(CodegenUtils.getSyntheticTypeNamespace()) - && emittedSchemas.add(shape.getId())) { - ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", - writer -> new SchemaGenerator(ctx, shape).accept(writer)); - } return null; } @Override public Void structureShape(StructureShape shape) { if (shape.getId().getNamespace().equals(CodegenUtils.getSyntheticTypeNamespace())) { - // For synthetic clones with an archetype, generate the schema under - // the archetype's name. Stub synthetics (no archetype) are skipped. - if (!settings.useLegacySerde() && !CodegenUtils.isStubSynthetic(shape)) { - var archetypeId = shape.getTrait(Synthetic.class).get().getArchetype().get(); - if (emittedSchemas.add(archetypeId)) { - var archetype = model.expectShape(archetypeId, StructureShape.class); - ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", - writer -> new SchemaGenerator(ctx, archetype).accept(writer)); - } - } return null; } writers.useShapeWriter(shape, writer -> new StructureGenerator(ctx, writer, shape, protocolGenerator).run()); - if (!settings.useLegacySerde() && emittedSchemas.add(shape.getId())) { - ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", - writer -> new SchemaGenerator(ctx, shape).accept(writer)); - } - return null; } @@ -463,10 +449,6 @@ public Void stringShape(StringShape shape) { if (shape.hasTrait(EnumTrait.class)) { writers.useShapeWriter(shape, writer -> new EnumGenerator(symbolProvider, writer, shape).run()); } - if (!settings.useLegacySerde() && emittedSchemas.add(shape.getId())) { - ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", - writer -> new SchemaGenerator(ctx, shape).accept(writer)); - } return null; } @@ -476,10 +458,6 @@ public Void unionShape(UnionShape shape) { writers.useShapeWriter(shape, generator::generateUnion); writers.useShapeExportedTestWriter(shape, generator::generateUnionExamples); - if (!settings.useLegacySerde() && emittedSchemas.add(shape.getId())) { - ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", - writer -> new SchemaGenerator(ctx, shape).accept(writer)); - } return null; } @@ -543,10 +521,6 @@ public Void serviceShape(ServiceShape shape) { @Override public Void intEnumShape(IntEnumShape shape) { writers.useShapeWriter(shape, writer -> new IntEnumGenerator(symbolProvider, writer, shape).run()); - if (!settings.useLegacySerde() && emittedSchemas.add(shape.getId())) { - ctx.writerDelegator().useFileWriter("schemas/schemas.go", settings.getModuleName() + "/schemas", - writer -> new SchemaGenerator(ctx, shape).accept(writer)); - } return null; } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java index ef7f28683..40d302201 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/TypeRegistry.java @@ -44,7 +44,9 @@ public void accept(GoWriter writer) { if (sorted.stream().anyMatch(Prelude::isPublicPreludeShape)) { writer.addUseImports(SmithyGoDependency.SMITHY_PRELUDE); } - writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); + if (sorted.stream().anyMatch(s -> !Prelude.isPublicPreludeShape(s))) { + writer.addImport(ctx.settings().getModuleName() + "/schemas", "schemas"); + } writer.writeGoTemplate(""" // TypeRegistry is the type registry for this service. var TypeRegistry = &smithy.TypeRegistry{ diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java index 36ca0a41c..20a4f849a 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/ListDeserializer.java @@ -107,6 +107,8 @@ private Writable renderZeroValue() { return switch (member.getType()) { case STRUCTURE -> goTemplate("vv = $T{}", ctx.symbolProvider().toSymbol(member)); + case LIST, SET, MAP -> + goTemplate("vv = nil"); default -> goTemplate(""); }; } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java index 6db1b7aec..5b89bc132 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/MapDeserializer.java @@ -109,6 +109,8 @@ private Writable renderZeroValue() { return switch (value.getType()) { case STRUCTURE -> goTemplate("vv = $T{}", ctx.symbolProvider().toSymbol(value)); + case LIST, SET, MAP -> + goTemplate("vv = nil"); default -> goTemplate(""); }; } @@ -129,6 +131,9 @@ private Writable renderSparseCast() { case DOCUMENT -> renderDocumentCast(); + case STRUCTURE -> goTemplate("func() *$T { cp := vv; return &cp }()", + ctx.symbolProvider().toSymbol(value)); + default -> goTemplate("&vv"); }; } diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java index e3cf71f0b..629fc7f86 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/StructureSerializer.java @@ -93,7 +93,7 @@ private void generateSerializeMember(GoWriter writer, MemberShape member, Shape case TIMESTAMP -> writeScalar(writer, isNillable, ident, "", "WriteTime", schemaName); case BLOB -> - writer.write("s.WriteBlob($L, $L)", schemaName, ident); + writer.write("if $2L != nil { s.WriteBlob($1L, $2L) }", schemaName, ident); case LIST, SET, MAP, UNION -> writer.write("serialize$L(s, $L, $L)", target.getId().getName(), schemaName, ident); diff --git a/smithy-http-protocols/rpcv2/rpcv2.go b/smithy-http-protocols/rpcv2/rpcv2.go index f8abbe86d..39b832cfa 100644 --- a/smithy-http-protocols/rpcv2/rpcv2.go +++ b/smithy-http-protocols/rpcv2/rpcv2.go @@ -77,6 +77,10 @@ func (p *Protocol) SerializeRequest( return nil } + if schema.Input == nil { + return nil + } + ss := internalcbor.NewShapeSerializer() in.Serialize(ss) From e0ef14952c84948da991865099a7c80cedc474d0 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Fri, 22 May 2026 15:39:56 -0400 Subject: [PATCH 27/38] internalize the event stream exported member --- aws-protocols/awsjson/awsjson.go | 26 +++++++++++++++++++++++--- aws-protocols/awsquery/awsquery.go | 27 ++++++++++++++++++++++++++- aws-protocols/ec2query/ec2query.go | 27 ++++++++++++++++++++++++++- aws-protocols/restjson1/restjson1.go | 24 ++++++++++++++++++++++-- aws-protocols/restxml/restxml.go | 24 ++++++++++++++++++++++-- smithy-http-protocols/rpcv2/rpcv2.go | 24 ++++++++++++++++++++++-- 6 files changed, 141 insertions(+), 11 deletions(-) diff --git a/aws-protocols/awsjson/awsjson.go b/aws-protocols/awsjson/awsjson.go index a23f75303..6b94f8035 100644 --- a/aws-protocols/awsjson/awsjson.go +++ b/aws-protocols/awsjson/awsjson.go @@ -32,7 +32,7 @@ func New10(service *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Proto version: "1.0", queryCompatible: qc, serviceName: service.Schema.ID().Name, - Codec: &internales.Codec{ + eventstream: &internales.Codec{ Serializer: func() smithy.ShapeSerializer { return internaljson.NewShapeSerializer() }, Deserializer: func(p []byte) smithy.ShapeDeserializer { return internaljson.NewShapeDeserializer(p) }, ContentType: "application/json", @@ -51,7 +51,7 @@ func New11(service *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Proto version: "1.1", queryCompatible: qc, serviceName: service.Schema.ID().Name, - Codec: &internales.Codec{ + eventstream: &internales.Codec{ Serializer: func() smithy.ShapeSerializer { return internaljson.NewShapeSerializer() }, Deserializer: func(p []byte) smithy.ShapeDeserializer { return internaljson.NewShapeDeserializer(p) }, ContentType: "application/json", @@ -65,7 +65,7 @@ type Protocol struct { queryCompatible bool serviceName string - *internales.Codec + eventstream *internales.Codec } var _ smithyhttp.ClientProtocol = (*Protocol)(nil) @@ -160,6 +160,26 @@ func (*Protocol) HasInitialEventMessage() bool { return true } +// SerializeEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) SerializeEventMessage(schema, variant *smithy.Schema, v smithy.Serializable, w io.Writer) error { + return p.eventstream.SerializeEventMessage(schema, variant, v, w) +} + +// DeserializeEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) DeserializeEventMessage(schema *smithy.Schema, types *smithy.TypeRegistry, r io.Reader) (smithy.Deserializable, error) { + return p.eventstream.DeserializeEventMessage(schema, types, r) +} + +// SerializeInitialRequest implements [smithyhttp.ClientProtocol]. +func (p *Protocol) SerializeInitialRequest(schema *smithy.Schema, v smithy.Serializable, w io.Writer) error { + return p.eventstream.SerializeInitialRequest(schema, v, w) +} + +// DeserializeInitialResponse implements [smithyhttp.ClientProtocol]. +func (p *Protocol) DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, out smithy.Deserializable) error { + return p.eventstream.DeserializeInitialResponse(schema, r, out) +} + // this handles both awsJson 1.0 and 1.1 - the only thing that 1.1 adds is // error shape renaming (basically not having the namespace) but both versions // of the protocol are supposed to support this anyway so it doesn't matter diff --git a/aws-protocols/awsquery/awsquery.go b/aws-protocols/awsquery/awsquery.go index bf126ad1d..89d671458 100644 --- a/aws-protocols/awsquery/awsquery.go +++ b/aws-protocols/awsquery/awsquery.go @@ -21,7 +21,7 @@ type ProtocolOptions struct{} // Protocol implements aws.protocols#awsQuery. type Protocol struct { - internales.NoEventStream + eventstream internales.NoEventStream version string } @@ -43,6 +43,31 @@ func (*Protocol) ID() smithy.ShapeID { return smithy.ShapeID{Namespace: "aws.protocols", Name: "awsQuery"} } +// HasInitialEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) HasInitialEventMessage() bool { + return p.eventstream.HasInitialEventMessage() +} + +// SerializeEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) SerializeEventMessage(schema, variant *smithy.Schema, v smithy.Serializable, w io.Writer) error { + return p.eventstream.SerializeEventMessage(schema, variant, v, w) +} + +// DeserializeEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) DeserializeEventMessage(schema *smithy.Schema, types *smithy.TypeRegistry, r io.Reader) (smithy.Deserializable, error) { + return p.eventstream.DeserializeEventMessage(schema, types, r) +} + +// SerializeInitialRequest implements [smithyhttp.ClientProtocol]. +func (p *Protocol) SerializeInitialRequest(schema *smithy.Schema, v smithy.Serializable, w io.Writer) error { + return p.eventstream.SerializeInitialRequest(schema, v, w) +} + +// DeserializeInitialResponse implements [smithyhttp.ClientProtocol]. +func (p *Protocol) DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, out smithy.Deserializable) error { + return p.eventstream.DeserializeInitialResponse(schema, r, out) +} + // SerializeRequest serializes a request for awsQuery. func (p *Protocol) SerializeRequest( ctx context.Context, diff --git a/aws-protocols/ec2query/ec2query.go b/aws-protocols/ec2query/ec2query.go index efdc6dd24..d62ed42f8 100644 --- a/aws-protocols/ec2query/ec2query.go +++ b/aws-protocols/ec2query/ec2query.go @@ -20,7 +20,7 @@ type ProtocolOptions struct{} // Protocol implements aws.protocols#ec2Query. type Protocol struct { - internales.NoEventStream + eventstream internales.NoEventStream version string } @@ -42,6 +42,31 @@ func (*Protocol) ID() smithy.ShapeID { return smithy.ShapeID{Namespace: "aws.protocols", Name: "ec2Query"} } +// HasInitialEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) HasInitialEventMessage() bool { + return p.eventstream.HasInitialEventMessage() +} + +// SerializeEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) SerializeEventMessage(schema, variant *smithy.Schema, v smithy.Serializable, w io.Writer) error { + return p.eventstream.SerializeEventMessage(schema, variant, v, w) +} + +// DeserializeEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) DeserializeEventMessage(schema *smithy.Schema, types *smithy.TypeRegistry, r io.Reader) (smithy.Deserializable, error) { + return p.eventstream.DeserializeEventMessage(schema, types, r) +} + +// SerializeInitialRequest implements [smithyhttp.ClientProtocol]. +func (p *Protocol) SerializeInitialRequest(schema *smithy.Schema, v smithy.Serializable, w io.Writer) error { + return p.eventstream.SerializeInitialRequest(schema, v, w) +} + +// DeserializeInitialResponse implements [smithyhttp.ClientProtocol]. +func (p *Protocol) DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, out smithy.Deserializable) error { + return p.eventstream.DeserializeInitialResponse(schema, r, out) +} + // SerializeRequest serializes a request for ec2Query. func (p *Protocol) SerializeRequest( ctx context.Context, diff --git a/aws-protocols/restjson1/restjson1.go b/aws-protocols/restjson1/restjson1.go index ada360a51..77d521df4 100644 --- a/aws-protocols/restjson1/restjson1.go +++ b/aws-protocols/restjson1/restjson1.go @@ -25,7 +25,7 @@ func New(_ *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Protocol { fn(&o) } return &Protocol{ - Codec: &internales.Codec{ + eventstream: &internales.Codec{ Serializer: func() smithy.ShapeSerializer { return internaljson.NewShapeSerializer() }, Deserializer: func(p []byte) smithy.ShapeDeserializer { return internaljson.NewShapeDeserializer(p) }, ContentType: "application/json", @@ -35,7 +35,7 @@ func New(_ *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Protocol { // Protocol implements aws.protocols#restJson1. type Protocol struct { - *internales.Codec + eventstream *internales.Codec } var _ smithyhttp.ClientProtocol = (*Protocol)(nil) @@ -125,6 +125,26 @@ func (*Protocol) HasInitialEventMessage() bool { return false } +// SerializeEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) SerializeEventMessage(schema, variant *smithy.Schema, v smithy.Serializable, w io.Writer) error { + return p.eventstream.SerializeEventMessage(schema, variant, v, w) +} + +// DeserializeEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) DeserializeEventMessage(schema *smithy.Schema, types *smithy.TypeRegistry, r io.Reader) (smithy.Deserializable, error) { + return p.eventstream.DeserializeEventMessage(schema, types, r) +} + +// SerializeInitialRequest implements [smithyhttp.ClientProtocol]. +func (p *Protocol) SerializeInitialRequest(schema *smithy.Schema, v smithy.Serializable, w io.Writer) error { + return p.eventstream.SerializeInitialRequest(schema, v, w) +} + +// DeserializeInitialResponse implements [smithyhttp.ClientProtocol]. +func (p *Protocol) DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, out smithy.Deserializable) error { + return p.eventstream.DeserializeInitialResponse(schema, r, out) +} + func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithyhttp.Response) error { var errorBuffer bytes.Buffer if _, err := io.Copy(&errorBuffer, response.Body); err != nil { diff --git a/aws-protocols/restxml/restxml.go b/aws-protocols/restxml/restxml.go index bc4133cc7..88a731070 100644 --- a/aws-protocols/restxml/restxml.go +++ b/aws-protocols/restxml/restxml.go @@ -16,7 +16,7 @@ import ( // Protocol implements aws.protocols#restXml. type Protocol struct { - *internales.Codec + eventstream *internales.Codec seropts func(*internalxml.ShapeSerializerOptions) } @@ -43,7 +43,7 @@ func New(service *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Protoco } return &Protocol{ - Codec: &internales.Codec{ + eventstream: &internales.Codec{ Serializer: func() smithy.ShapeSerializer { if xmlOpts != nil { return internalxml.NewShapeSerializer(xmlOpts) @@ -67,6 +67,26 @@ func (*Protocol) HasInitialEventMessage() bool { return false } +// SerializeEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) SerializeEventMessage(schema, variant *smithy.Schema, v smithy.Serializable, w io.Writer) error { + return p.eventstream.SerializeEventMessage(schema, variant, v, w) +} + +// DeserializeEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) DeserializeEventMessage(schema *smithy.Schema, types *smithy.TypeRegistry, r io.Reader) (smithy.Deserializable, error) { + return p.eventstream.DeserializeEventMessage(schema, types, r) +} + +// SerializeInitialRequest implements [smithyhttp.ClientProtocol]. +func (p *Protocol) SerializeInitialRequest(schema *smithy.Schema, v smithy.Serializable, w io.Writer) error { + return p.eventstream.SerializeInitialRequest(schema, v, w) +} + +// DeserializeInitialResponse implements [smithyhttp.ClientProtocol]. +func (p *Protocol) DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, out smithy.Deserializable) error { + return p.eventstream.DeserializeInitialResponse(schema, r, out) +} + // SerializeRequest serializes a request for restxml. func (p *Protocol) SerializeRequest( ctx context.Context, diff --git a/smithy-http-protocols/rpcv2/rpcv2.go b/smithy-http-protocols/rpcv2/rpcv2.go index 39b832cfa..a503d18be 100644 --- a/smithy-http-protocols/rpcv2/rpcv2.go +++ b/smithy-http-protocols/rpcv2/rpcv2.go @@ -25,7 +25,7 @@ type Protocol struct { queryCompatible bool serviceName string - *internales.Codec + eventstream *internales.Codec } var _ smithyhttp.ClientProtocol = (*Protocol)(nil) @@ -43,7 +43,7 @@ func NewCBOR(service *smithy.ServiceSchema, opts ...func(*ProtocolOptions)) *Pro return &Protocol{ queryCompatible: qc, serviceName: service.Schema.ID().Name, - Codec: &internales.Codec{ + eventstream: &internales.Codec{ Serializer: func() smithy.ShapeSerializer { return internalcbor.NewShapeSerializer() }, Deserializer: func(p []byte) smithy.ShapeDeserializer { return internalcbor.NewShapeDeserializer(p) }, ContentType: "application/cbor", @@ -142,6 +142,26 @@ func (*Protocol) HasInitialEventMessage() bool { return true } +// SerializeEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) SerializeEventMessage(schema, variant *smithy.Schema, v smithy.Serializable, w io.Writer) error { + return p.eventstream.SerializeEventMessage(schema, variant, v, w) +} + +// DeserializeEventMessage implements [smithyhttp.ClientProtocol]. +func (p *Protocol) DeserializeEventMessage(schema *smithy.Schema, types *smithy.TypeRegistry, r io.Reader) (smithy.Deserializable, error) { + return p.eventstream.DeserializeEventMessage(schema, types, r) +} + +// SerializeInitialRequest implements [smithyhttp.ClientProtocol]. +func (p *Protocol) SerializeInitialRequest(schema *smithy.Schema, v smithy.Serializable, w io.Writer) error { + return p.eventstream.SerializeInitialRequest(schema, v, w) +} + +// DeserializeInitialResponse implements [smithyhttp.ClientProtocol]. +func (p *Protocol) DeserializeInitialResponse(schema *smithy.Schema, r io.Reader, out smithy.Deserializable) error { + return p.eventstream.DeserializeInitialResponse(schema, r, out) +} + func (p *Protocol) deserializeError(types *smithy.TypeRegistry, response *smithyhttp.Response) error { var errorBuffer bytes.Buffer if _, err := io.Copy(&errorBuffer, response.Body); err != nil { From 07813b21e057c46163827389d07d776feb9e6327 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Fri, 22 May 2026 17:26:09 -0400 Subject: [PATCH 28/38] fixup event stream tests --- .../EventStreamProtocolTestGenerator.java | 10 +------- .../serde2/Serde2EventStreamMiddleware.java | 4 +++- eventstream/deserializer.go | 13 ++++++++++- eventstream/serializer.go | 23 +++++++++++++++++++ schema.go | 7 +++++- 5 files changed, 45 insertions(+), 12 deletions(-) diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/EventStreamProtocolTestGenerator.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/EventStreamProtocolTestGenerator.java index d1a08ea00..345a25154 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/EventStreamProtocolTestGenerator.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/integration/EventStreamProtocolTestGenerator.java @@ -61,15 +61,7 @@ public class EventStreamProtocolTestGenerator { // value is treated as an unknown event (UnknownUnionMember) rather // than surfacing an error. "MalformedEventTypeOutput", - "DuplexMalformedEventTypeOutput", - - // FUTURE(serde2): released protocol generation does not handle - // "implicit" payload members (i.e. @eventHeader + no @eventPayload + - // other members), this is fixed in serde2 so just skip for now. - "HeadersAndImplicitPayloadOutput", - "DuplexHeadersAndImplicitPayloadOutput", - "HeadersAndImplicitPayloadInput", - "DuplexHeadersAndImplicitPayloadInput" + "DuplexMalformedEventTypeOutput" ); private final GoSettings settings; diff --git a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2EventStreamMiddleware.java b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2EventStreamMiddleware.java index ff9cc9d9f..8b7a25e43 100644 --- a/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2EventStreamMiddleware.java +++ b/codegen/smithy-go-codegen/src/main/java/software/amazon/smithy/go/codegen/serde2/Serde2EventStreamMiddleware.java @@ -328,7 +328,9 @@ private Writable v2FuncBody( ? (Writable) w -> w.write("stream.Reader = eventReader") : (Writable) w -> {}), Map.entry("asyncError", outputInfo.isPresent() - ? (Writable) w -> w.write("asyncResult <- deserializeResult{err: err}") + ? (Writable) w -> w.write(""" + asyncResult <- deserializeResult{err: err} + middleware.AddEventStreamOutputToMetadata(&md, output)""") : (Writable) w -> {}), Map.entry("asyncPipe", outputInfo.isPresent() ? (Writable) w -> w.write("asyncResult <- deserializeResult{reader: resp.Body}") diff --git a/eventstream/deserializer.go b/eventstream/deserializer.go index 7cd0039b7..8bc931a32 100644 --- a/eventstream/deserializer.go +++ b/eventstream/deserializer.go @@ -26,6 +26,7 @@ type ShapeDeserializer struct { inBody bool hasPayload bool + hasBody bool } var _ smithy.ShapeDeserializer = (*ShapeDeserializer)(nil) @@ -50,6 +51,8 @@ func (d *ShapeDeserializer) ReadStruct(s *smithy.Schema) error { } if isEventBound(m) { d.bindings = append(d.bindings, m) + } else { + d.hasBody = true } } return nil @@ -66,9 +69,12 @@ func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { // like httpbinding, throw back the bound stuff first before we drop into // the body - if d.bindIdx < len(d.bindings) { + for d.bindIdx < len(d.bindings) { m := d.bindings[d.bindIdx] d.bindIdx++ + if isEventHeader(m) && d.Message.Headers.Get(m.MemberName()) == nil { + continue + } d.inBindings = true return m, nil } @@ -79,6 +85,11 @@ func (d *ShapeDeserializer) ReadStructMember() (*smithy.Schema, error) { return nil, nil } + if !d.hasBody { + d.depth-- + return nil, nil + } + if !d.inBody { d.inBody = true if err := d.inner.ReadStruct(d.schema); err != nil { diff --git a/eventstream/serializer.go b/eventstream/serializer.go index 97e20172f..0ea778d83 100644 --- a/eventstream/serializer.go +++ b/eventstream/serializer.go @@ -16,6 +16,8 @@ type ShapeSerializer struct { inner smithy.ShapeSerializer contentType string // may be inflenced by bindings + depth int + hasBody bool } var _ smithy.ShapeSerializer = (*ShapeSerializer)(nil) @@ -143,10 +145,31 @@ func (s *ShapeSerializer) WriteBigFloat(schema *smithy.Schema, v *big.Float) { // WriteStruct implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) WriteStruct(schema *smithy.Schema) { + s.depth++ + if s.depth > 1 { + s.inner.WriteStruct(schema) + return + } + // At depth 1 (the event struct itself), start a JSON body if there are + // implicit body members (members without @eventHeader or @eventPayload). + for _, m := range schema.Members() { + if !isEventBound(m) { + s.inner.WriteStruct(schema) + s.hasBody = true + return + } + } } // CloseStruct implements [smithy.ShapeSerializer]. func (s *ShapeSerializer) CloseStruct() { + if s.depth > 1 || s.hasBody { + s.inner.CloseStruct() + } + if s.depth == 1 { + s.hasBody = false + } + s.depth-- } // WriteUnion implements [smithy.ShapeSerializer]. diff --git a/schema.go b/schema.go index dddfeae6d..ff6ba1ac8 100644 --- a/schema.go +++ b/schema.go @@ -297,6 +297,11 @@ func schemaTrait[T Trait](s *Schema, directOnly bool) (T, bool) { return tt, ok } +// indexStreaming is the indexed trait slot for @streaming, mirrored from +// traits.indexStreaming. We can't import the traits package from here due to a +// circular dependency. +const indexStreaming = 17 + func isEventStream(s *Schema) bool { if s == nil { return false @@ -305,7 +310,7 @@ func isEventStream(s *Schema) bool { if m.typ != ShapeTypeUnion { continue } - if _, ok := m.traits[ShapeID{Namespace: "smithy.api", Name: "streaming"}]; ok { + if len(m.indexed) > indexStreaming && m.indexed[indexStreaming] != nil { return true } } From fb5f09edec56115a97852511ef670aad0acf7210 Mon Sep 17 00:00:00 2001 From: Luc Talatinian Date: Fri, 22 May 2026 17:40:22 -0400 Subject: [PATCH 29/38] do not mutate trait state --- .../internal/httpbinding/deserializer.go | 34 +++++++++---------- .../internal/httpbinding/serializer.go | 20 +++++------ 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/aws-protocols/internal/httpbinding/deserializer.go b/aws-protocols/internal/httpbinding/deserializer.go index 26a187d31..cdfa0d4ac 100644 --- a/aws-protocols/internal/httpbinding/deserializer.go +++ b/aws-protocols/internal/httpbinding/deserializer.go @@ -156,13 +156,13 @@ func (d *ShapeDeserializer) ReadBool(s *smithy.Schema, v *bool) error { return d.body.ReadBool(s, v) } - trait, _ := isHTTPHeader(s) + name, _ := isHTTPHeader(s) var hv string if d.inHeaderList { hv = d.nextHeaderValue() } else { - hv = d.response.Header.Get(trait.Name) + hv = d.response.Header.Get(name) } n, err := strconv.ParseBool(hv) @@ -249,8 +249,8 @@ func (d *ShapeDeserializer) ReadTime(s *smithy.Schema, v *time.Time) error { } func (d *ShapeDeserializer) readHeaderTime(s *smithy.Schema) (time.Time, error) { - h, _ := isHTTPHeader(s) - hv := d.response.Header.Get(h.Name) + name, _ := isHTTPHeader(s) + hv := d.response.Header.Get(name) t, err := parseTimestamp(s, "http-date", hv) if err != nil { return time.Time{}, err @@ -280,12 +280,12 @@ func (d *ShapeDeserializer) ReadBlob(s *smithy.Schema, v *[]byte) error { return nil } - h, ok := isHTTPHeader(s) + name, ok := isHTTPHeader(s) if !ok { return fmt.Errorf("ReadBlob: unhandled http binding") } - hv := d.response.Header.Get(h.Name) + hv := d.response.Header.Get(name) b, err := base64.StdEncoding.DecodeString(hv) if err != nil { return err @@ -300,7 +300,7 @@ func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { return d.body.ReadList(s) } - h, ok := isHTTPHeader(s) + name, ok := isHTTPHeader(s) if !ok { return fmt.Errorf("ReadList called outside of payload / http binding") } @@ -308,14 +308,14 @@ func (d *ShapeDeserializer) ReadList(s *smithy.Schema) error { d.inHeaderList = true d.headerListIdx = 0 if s.ListMember() != nil && s.ListMember().Type() == smithy.ShapeTypeTimestamp && timestampFormat(s.ListMember(), "http-date") == "http-date" { - vs, err := smithyhttp.SplitHTTPDateTimestampHeaderListValues(d.response.Header.Values(h.Name)) + vs, err := smithyhttp.SplitHTTPDateTimestampHeaderListValues(d.response.Header.Values(name)) if err != nil { return err } d.headerListValues = vs } else { - vs, err := smithyhttp.SplitHeaderListValues(d.response.Header.Values(h.Name)) + vs, err := smithyhttp.SplitHeaderListValues(d.response.Header.Values(name)) if err != nil { return err } @@ -394,8 +394,8 @@ func (d *ShapeDeserializer) ReadUnion(s *smithy.Schema) (*smithy.Schema, error) } func (d *ShapeDeserializer) isBindingSet(schema *smithy.Schema) bool { - if trait, ok := isHTTPHeader(schema); ok { - return len(d.response.Header.Values(trait.Name)) > 0 + if name, ok := isHTTPHeader(schema); ok { + return len(d.response.Header.Values(name)) > 0 } if trait, ok := isHTTPPrefixHeaders(schema); ok { @@ -420,9 +420,9 @@ func (d *ShapeDeserializer) isBindingSet(schema *smithy.Schema) bool { } func (d *ShapeDeserializer) readHeaderString(s *smithy.Schema) (string, error) { - trait, _ := isHTTPHeader(s) + name, _ := isHTTPHeader(s) - hv := d.response.Header.Get(trait.Name) + hv := d.response.Header.Get(name) if _, ok := smithy.SchemaTrait[*traits.MediaType](s); ok { b, err := base64.StdEncoding.DecodeString(hv) if err != nil { @@ -444,13 +444,13 @@ type intn interface { } func readHeaderInt[T intn](d *ShapeDeserializer, s *smithy.Schema, v *T) error { - trait, _ := isHTTPHeader(s) + name, _ := isHTTPHeader(s) var hv string if d.inHeaderList { hv = d.nextHeaderValue() } else { - hv = d.response.Header.Get(trait.Name) + hv = d.response.Header.Get(name) } n, err := strconv.ParseInt(hv, 10, 64) @@ -467,13 +467,13 @@ type floatn interface { } func readHeaderFloat[T floatn](d *ShapeDeserializer, s *smithy.Schema, v *T) error { - trait, _ := isHTTPHeader(s) + name, _ := isHTTPHeader(s) var hv string if d.inHeaderList { hv = d.nextHeaderValue() } else { - hv = d.response.Header.Get(trait.Name) + hv = d.response.Header.Get(name) } n, err := strconv.ParseFloat(hv, 64) diff --git a/aws-protocols/internal/httpbinding/serializer.go b/aws-protocols/internal/httpbinding/serializer.go index ce0584379..f2512eade 100644 --- a/aws-protocols/internal/httpbinding/serializer.go +++ b/aws-protocols/internal/httpbinding/serializer.go @@ -177,8 +177,8 @@ func (s *ShapeSerializer) resolveBinding(schema *smithy.Schema) (bind, string) { if s.listMode == listModeQuery { return bindQueryList, s.listName } - if h, ok := isHTTPHeader(schema); ok { - return bindHeader, h.Name + if name, ok := isHTTPHeader(schema); ok { + return bindHeader, name } if isHTTPLabel(schema) { return bindLabel, schema.MemberName() @@ -196,12 +196,12 @@ func (s *ShapeSerializer) Bytes() []byte { return s.input.Bytes() } -func isHTTPHeader(schema *smithy.Schema) (*traits.HTTPHeader, bool) { +func isHTTPHeader(schema *smithy.Schema) (string, bool) { h, ok := smithy.SchemaTrait[*traits.HTTPHeader](schema) - if ok { - h.Name = http.CanonicalHeaderKey(h.Name) + if !ok { + return "", false } - return h, ok + return http.CanonicalHeaderKey(h.Name), true } func isHTTPLabel(schema *smithy.Schema) bool { @@ -452,8 +452,8 @@ func (s *ShapeSerializer) WriteBlob(schema *smithy.Schema, v []byte) { } return } - if h, ok := isHTTPHeader(schema); ok { - s.encoder.SetHeader(h.Name).Blob(v) + if name, ok := isHTTPHeader(schema); ok { + s.encoder.SetHeader(name).Blob(v) return } s.input.WriteBlob(schema, v) @@ -486,9 +486,9 @@ func (s *ShapeSerializer) WriteList(schema *smithy.Schema) { s.listName = s.currentKey return } - if h, ok := isHTTPHeader(schema); ok { + if name, ok := isHTTPHeader(schema); ok { s.listMode = listModeHeader - s.listName = h.Name + s.listName = name s.listHasItems = false return } From 6792656ad0022c86bcdf5e6aa02e03556013568f Mon Sep 17 00:00:00 2001 From: Adwait Kumar Singh Date: Fri, 29 May 2026 08:21:07 -0700 Subject: [PATCH 30/38] serde2 runtime optimizations (#668) --- .../internal/httpbinding/serializer.go | 9 +- aws-protocols/internal/json/benchmark_test.go | 160 ++++- aws-protocols/internal/json/ext.go | 117 ++++ aws-protocols/internal/json/fuzz_test.go | 151 +++-- .../internal/json/internal/stdlib/stdlib.go | 1 + aws-protocols/internal/json/json.test | Bin 0 -> 5895026 bytes aws-protocols/internal/json/parser.go | 430 ++++++++++--- aws-protocols/internal/json/scanner.go | 190 ------ aws-protocols/internal/json/scanner_test.go | 26 +- .../internal/json/shape_deserializer.go | 366 ++++++++++- .../internal/json/shape_serializer.go | 603 +++++++++++------- .../FuzzParserDifferential/8a874704f001c1c7 | 2 + .../internal/query/shape_serializer.go | 14 +- .../internal/xml/shape_serializer.go | 12 +- .../smithy/go/codegen/UnionGenerator.java | 4 +- .../go/codegen/serde2/UnionSerializer.java | 85 ++- eventstream/serializer.go | 9 +- internal/serde/stack.go | 9 + schema.go | 16 +- schema_ext.go | 37 ++ serde.go | 3 +- .../internal/cbor/shape_serializer.go | 6 +- 22 files changed, 1582 insertions(+), 668 deletions(-) create mode 100644 aws-protocols/internal/json/ext.go create mode 100755 aws-protocols/internal/json/json.test create mode 100644 aws-protocols/internal/json/testdata/fuzz/FuzzParserDifferential/8a874704f001c1c7 create mode 100644 schema_ext.go diff --git a/aws-protocols/internal/httpbinding/serializer.go b/aws-protocols/internal/httpbinding/serializer.go index f2512eade..9eadd05d9 100644 --- a/aws-protocols/internal/httpbinding/serializer.go +++ b/aws-protocols/internal/httpbinding/serializer.go @@ -595,8 +595,13 @@ func (s *ShapeSerializer) CloseStruct() { } // WriteUnion implements [smithy.ShapeSerializer]. -func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema, v smithy.Serializable) { - s.input.WriteUnion(schema, variant, v) +func (s *ShapeSerializer) WriteUnion(schema, variant *smithy.Schema) { + s.input.WriteUnion(schema, variant) +} + +// CloseUnion implements [smithy.ShapeSerializer]. +func (s *ShapeSerializer) CloseUnion() { + s.input.CloseUnion() } // WriteNil implements [smithy.ShapeSerializer]. diff --git a/aws-protocols/internal/json/benchmark_test.go b/aws-protocols/internal/json/benchmark_test.go index ac40e383e..6fd9f6637 100644 --- a/aws-protocols/internal/json/benchmark_test.go +++ b/aws-protocols/internal/json/benchmark_test.go @@ -466,6 +466,7 @@ func init() { func newDeserialize(data []byte) (*GetItemOutput, error) { d := NewShapeDeserializer(data) + defer d.Close() var out GetItemOutput if err := newDeserializeGetItemOutput(d, &out); err != nil { return nil, err @@ -473,8 +474,8 @@ func newDeserialize(data []byte) (*GetItemOutput, error) { return &out, nil } -func newDeserializeGetItemOutput(d smithy.ShapeDeserializer, v *GetItemOutput) error { - return smithy.ReadStruct(d, schemaGetItemOutput, func(ms *smithy.Schema) error { +func newDeserializeGetItemOutput(d *ShapeDeserializer, v *GetItemOutput) error { + return d.DirectReadStruct(schemaGetItemOutput, func(ms *smithy.Schema) error { switch ms { case schemaGetItemOutput_ConsumedCapacity: v.ConsumedCapacity = &ConsumedCapacity{} @@ -486,8 +487,8 @@ func newDeserializeGetItemOutput(d smithy.ShapeDeserializer, v *GetItemOutput) e }) } -func newDeserializeConsumedCapacity(d smithy.ShapeDeserializer, v *ConsumedCapacity) error { - return smithy.ReadStruct(d, schemaConsumedCapacity, func(ms *smithy.Schema) error { +func newDeserializeConsumedCapacity(d *ShapeDeserializer, v *ConsumedCapacity) error { + return d.DirectReadStruct(schemaConsumedCapacity, func(ms *smithy.Schema) error { switch ms { case schemaConsumedCapacity_TableName: v.TableName = new(string) @@ -499,22 +500,22 @@ func newDeserializeConsumedCapacity(d smithy.ShapeDeserializer, v *ConsumedCapac }) } -func newDeserializeAttributeMap(d smithy.ShapeDeserializer, v *map[string]AttributeValue) error { - return smithy.ReadMap(d, schemaAttributeMap, func(key string) error { +func newDeserializeAttributeMap(d *ShapeDeserializer, v *map[string]AttributeValue) error { + return d.DirectReadMap(schemaAttributeMap, func(key string) error { var av AttributeValue if err := newDeserializeAttributeValue(d, &av); err != nil { return err } if *v == nil { - *v = map[string]AttributeValue{} + *v = make(map[string]AttributeValue, 8) } (*v)[key] = av return nil }) } -func newDeserializeAttributeValue(d smithy.ShapeDeserializer, v *AttributeValue) error { - return smithy.ReadUnion(d, schemaAttributeValue, func(ms *smithy.Schema) error { +func newDeserializeAttributeValue(d *ShapeDeserializer, v *AttributeValue) error { + return d.DirectReadUnion(schemaAttributeValue, func(ms *smithy.Schema) error { switch ms { case schemaAttributeValue_S: vv := &AttributeValueMemberS{} @@ -539,7 +540,7 @@ func newDeserializeAttributeValue(d smithy.ShapeDeserializer, v *AttributeValue) case schemaAttributeValue_SS: vv := &AttributeValueMemberSS{} *v = vv - return smithy.ReadList(d, schemaStringSetAttributeValue, func() error { + return d.DirectReadList(schemaStringSetAttributeValue, func() error { var s string if err := d.ReadString(nil, &s); err != nil { return err @@ -550,7 +551,7 @@ func newDeserializeAttributeValue(d smithy.ShapeDeserializer, v *AttributeValue) case schemaAttributeValue_NS: vv := &AttributeValueMemberNS{} *v = vv - return smithy.ReadList(d, schemaNumberSetAttributeValue, func() error { + return d.DirectReadList(schemaNumberSetAttributeValue, func() error { var s string if err := d.ReadString(nil, &s); err != nil { return err @@ -561,7 +562,7 @@ func newDeserializeAttributeValue(d smithy.ShapeDeserializer, v *AttributeValue) case schemaAttributeValue_BS: vv := &AttributeValueMemberBS{} *v = vv - return smithy.ReadList(d, schemaBinarySetAttributeValue, func() error { + return d.DirectReadList(schemaBinarySetAttributeValue, func() error { var b []byte if err := d.ReadBlob(nil, &b); err != nil { return err @@ -572,7 +573,7 @@ func newDeserializeAttributeValue(d smithy.ShapeDeserializer, v *AttributeValue) case schemaAttributeValue_L: vv := &AttributeValueMemberL{} *v = vv - return smithy.ReadList(d, schemaListAttributeValue, func() error { + return d.DirectReadList(schemaListAttributeValue, func() error { var av AttributeValue if err := newDeserializeAttributeValue(d, &av); err != nil { return err @@ -583,7 +584,7 @@ func newDeserializeAttributeValue(d smithy.ShapeDeserializer, v *AttributeValue) case schemaAttributeValue_M: vv := &AttributeValueMemberM{} *v = vv - return smithy.ReadMap(d, schemaMapAttributeValue, func(key string) error { + return d.DirectReadMap(schemaMapAttributeValue, func(key string) error { var av AttributeValue if err := newDeserializeAttributeValue(d, &av); err != nil { return err @@ -629,6 +630,136 @@ func BenchmarkDeserialize_New(b *testing.B) { } } +// ========================================================================== +// Serialize benchmarks +// ========================================================================== + +func newSerialize(out *GetItemOutput) ([]byte, error) { + s := NewShapeSerializer() + newSerializeGetItemOutput(s, out) + b := s.Bytes() + s.Close() + return b, nil +} + +func newSerializeGetItemOutput(s *ShapeSerializer, v *GetItemOutput) { + s.WriteStruct(schemaGetItemOutput) + if v.ConsumedCapacity != nil { + newSerializeConsumedCapacity(s, schemaGetItemOutput_ConsumedCapacity, v.ConsumedCapacity) + } + if v.Item != nil { + newSerializeAttributeMap(s, schemaGetItemOutput_Item, v.Item) + } + s.CloseStruct() +} + +func newSerializeConsumedCapacity(s *ShapeSerializer, schema *smithy.Schema, v *ConsumedCapacity) { + s.WriteStruct(schema) + if v.TableName != nil { + s.WriteString(schemaConsumedCapacity_TableName, *v.TableName) + } + s.WriteFloat64(schemaConsumedCapacity_CapacityUnits, v.CapacityUnits) + s.CloseStruct() +} + +func newSerializeAttributeMap(s *ShapeSerializer, schema *smithy.Schema, m map[string]AttributeValue) { + s.WriteMap(schema) + for k, v := range m { + s.WriteKey(nil, k) + newSerializeAttributeValue(s, v) + } + s.CloseMap() +} + +func newSerializeAttributeValue(s *ShapeSerializer, v AttributeValue) { + switch av := v.(type) { + case *AttributeValueMemberS: + s.WriteUnion(schemaAttributeValue, schemaAttributeValue_S) + s.WriteString(schemaAttributeValue_S, av.Value) + s.CloseUnion() + case *AttributeValueMemberN: + s.WriteUnion(schemaAttributeValue, schemaAttributeValue_N) + s.WriteString(schemaAttributeValue_N, av.Value) + s.CloseUnion() + case *AttributeValueMemberB: + s.WriteUnion(schemaAttributeValue, schemaAttributeValue_B) + s.WriteBlob(schemaAttributeValue_B, av.Value) + s.CloseUnion() + case *AttributeValueMemberBOOL: + s.WriteUnion(schemaAttributeValue, schemaAttributeValue_BOOL) + s.WriteBool(schemaAttributeValue_BOOL, av.Value) + s.CloseUnion() + case *AttributeValueMemberNULL: + s.WriteUnion(schemaAttributeValue, schemaAttributeValue_NULL) + s.WriteBool(schemaAttributeValue_NULL, av.Value) + s.CloseUnion() + case *AttributeValueMemberSS: + s.WriteUnion(schemaAttributeValue, schemaAttributeValue_SS) + s.WriteList(schemaAttributeValue_SS) + for _, item := range av.Value { + s.WriteString(nil, item) + } + s.CloseList() + s.CloseUnion() + case *AttributeValueMemberNS: + s.WriteUnion(schemaAttributeValue, schemaAttributeValue_NS) + s.WriteList(schemaAttributeValue_NS) + for _, item := range av.Value { + s.WriteString(nil, item) + } + s.CloseList() + s.CloseUnion() + case *AttributeValueMemberBS: + s.WriteUnion(schemaAttributeValue, schemaAttributeValue_BS) + s.WriteList(schemaAttributeValue_BS) + for _, item := range av.Value { + s.WriteBlob(nil, item) + } + s.CloseList() + s.CloseUnion() + case *AttributeValueMemberL: + s.WriteUnion(schemaAttributeValue, schemaAttributeValue_L) + s.WriteList(schemaAttributeValue_L) + for _, item := range av.Value { + newSerializeAttributeValue(s, item) + } + s.CloseList() + s.CloseUnion() + case *AttributeValueMemberM: + s.WriteUnion(schemaAttributeValue, schemaAttributeValue_M) + s.WriteMap(schemaAttributeValue_M) + for k, item := range av.Value { + s.WriteKey(nil, k) + newSerializeAttributeValue(s, item) + } + s.CloseMap() + s.CloseUnion() + } +} + +var benchOutput *GetItemOutput + +func init() { + var err error + benchOutput, err = newDeserialize(benchPayload) + if err != nil { + panic(err) + } +} + +func BenchmarkSerialize_New(b *testing.B) { + b.ReportAllocs() + for b.Loop() { + out, err := newSerialize(benchOutput) + if err != nil { + b.Fatal(err) + } + if len(out) == 0 { + b.Fatal("empty") + } + } +} + // ========================================================================== // Large payload // ========================================================================== @@ -685,3 +816,4 @@ func BenchmarkLargePayload_New(b *testing.B) { } } } + diff --git a/aws-protocols/internal/json/ext.go b/aws-protocols/internal/json/ext.go new file mode 100644 index 000000000..82b74aed3 --- /dev/null +++ b/aws-protocols/internal/json/ext.go @@ -0,0 +1,117 @@ +package json + +import ( + "sort" + + "github.com/aws/smithy-go" + "github.com/aws/smithy-go/traits" +) + +type jsonExt struct { + jsonKey []byte // `,"memberName":` -- use [1:] when no comma needed + jsonNameKey []byte // `,"jsonName":` -- use [1:] when no comma needed (nil if no @jsonName) + + memberList []memberEntry + byteIndex *[256]int16 +} + +type memberEntry struct { + name string + schema *smithy.Schema +} + +func getExt(s *smithy.Schema) *jsonExt { + return smithy.SchemaExtension(s, smithy.ExtJSON, buildJSONExt) +} + +func buildJSONExt(s *smithy.Schema) *jsonExt { + ext := &jsonExt{} + + if name := s.MemberName(); name != "" { + ext.jsonKey = encodeJSONKey(name) + if jn, ok := smithy.SchemaTrait[*traits.JSONName](s); ok { + ext.jsonNameKey = encodeJSONKey(jn.Name) + } + } + + if members := s.Members(); len(members) > 0 { + names := make([]string, 0, len(members)) + for name := range members { + names = append(names, name) + } + sort.Strings(names) + + ext.memberList = make([]memberEntry, len(names)) + idx := &[256]int16{} + for i := range idx { + idx[i] = -1 + } + for pos, name := range names { + ext.memberList[pos] = memberEntry{name: name, schema: members[name]} + if len(name) > 0 { + b := name[0] + if idx[b] == -1 { + idx[b] = int16(pos) + } else { + idx[b] = -2 + } + } + } + ext.byteIndex = idx + } + + return ext +} + +func memberByBytes(s *smithy.Schema, name []byte) *smithy.Schema { + ext := getExt(s) + if ext.byteIndex == nil || len(name) == 0 { + return nil + } + idx := ext.byteIndex[name[0]] + if idx == -1 { + return nil + } + if idx >= 0 { + e := &ext.memberList[idx] + if len(e.name) == len(name) && e.name == string(name) { + return e.schema + } + return nil + } + for i := range ext.memberList { + e := &ext.memberList[i] + if len(e.name) == len(name) && e.name == string(name) { + return e.schema + } + } + return nil +} + +func encodeJSONKey(name string) []byte { + buf := make([]byte, 0, len(name)+4) + buf = append(buf, ',', '"') + for i := 0; i < len(name); i++ { + c := name[i] + switch c { + case '"': + buf = append(buf, '\\', '"') + case '\\': + buf = append(buf, '\\', '\\') + case '\n': + buf = append(buf, '\\', 'n') + case '\r': + buf = append(buf, '\\', 'r') + case '\t': + buf = append(buf, '\\', 't') + default: + if c < 0x20 { + buf = append(buf, '\\', 'u', '0', '0', "0123456789abcdef"[c>>4], "0123456789abcdef"[c&0xF]) + } else { + buf = append(buf, c) + } + } + } + buf = append(buf, '"', ':') + return buf +} diff --git a/aws-protocols/internal/json/fuzz_test.go b/aws-protocols/internal/json/fuzz_test.go index a0dc157cb..d556511b2 100644 --- a/aws-protocols/internal/json/fuzz_test.go +++ b/aws-protocols/internal/json/fuzz_test.go @@ -1,50 +1,23 @@ package json import ( + "encoding/json" + "fmt" "io" + "reflect" "testing" ) -// Adapted from the Go 1.24 standard library's encoding/json FuzzDecoderToken. -// -// https://cs.opensource.google/go/go/+/refs/tags/go1.24.0:src/encoding/json/fuzz_test.go +// FuzzParser checks that the parser doesn't crash on arbitrary input. func FuzzParser(f *testing.F) { - // Seed corpus from stdlib FuzzDecoderToken. - f.Add([]byte(`{ -"object": { - "slice": [ - 1, - 2.0, - "3", - [4], - {5: {}} - ] -}, -"slice": [[]], -"string": ":)", -"int": 1e5, -"float": 3e-9" -}`)) - - // Additional seeds exercising edge cases. - f.Add([]byte(`null`)) - f.Add([]byte(`true`)) - f.Add([]byte(`false`)) - f.Add([]byte(`0`)) - f.Add([]byte(`""`)) - f.Add([]byte(`"\u0000"`)) - f.Add([]byte(`"\uD834\uDD1E"`)) - f.Add([]byte(`{}`)) - f.Add([]byte(`[]`)) - f.Add([]byte(`{"a":1,"b":[2,3],"c":{"d":true}}`)) - f.Add([]byte(`[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]`)) - f.Add([]byte(`"\/""`)) - f.Add([]byte(`-1.23e+45`)) + for _, seed := range fuzzSeeds { + f.Add(seed) + } f.Fuzz(func(t *testing.T, b []byte) { p := parser{ - tok: scanner{p: b}, - parse: (*parser).parseValue, + p: b, + state: stValue, } for { _, err := p.Next() @@ -57,3 +30,109 @@ func FuzzParser(f *testing.F) { } }) } + +// FuzzParserDifferential compares our parser's accept/reject and parsed values +// against encoding/json. Any input accepted by one but rejected by the other +// (or producing different values) is a bug. +func FuzzParserDifferential(f *testing.F) { + for _, seed := range fuzzSeeds { + f.Add(seed) + } + + f.Fuzz(func(t *testing.T, b []byte) { + var stdVal any + stdErr := json.Unmarshal(b, &stdVal) + + p := parser{ + p: b, + state: stValue, + } + ourVal, ourErr := p.Value() + if ourErr == nil { + // parser.Value() reads one value from a stream and stops. It does + // not reject trailing content, but json.Unmarshal does. Check + // explicitly so the comparison is apples-to-apples. + for i := p.i; i < len(p.p); i++ { + if p.p[i] != ' ' && p.p[i] != '\t' && p.p[i] != '\n' && p.p[i] != '\r' { + ourErr = fmt.Errorf("trailing content at offset %d", i) + break + } + } + } + + stdOK := stdErr == nil + ourOK := ourErr == nil + + if stdOK != ourOK { + t.Errorf("accept/reject mismatch\ninput: %q\nstdlib ok: %v (err: %v)\nours ok: %v (err: %v)\nour value: %v", + b, stdOK, stdErr, ourOK, ourErr, ourVal) + return + } + + if !stdOK { + return + } + + if !reflect.DeepEqual(normalize(stdVal), normalize(ourVal)) { + t.Errorf("value mismatch\ninput: %q\nstdlib: %v\nours: %v", b, stdVal, ourVal) + } + }) +} + +// normalize recursively walks a parsed value tree. Both parsers produce the +// same types (float64 for numbers, string, bool, nil, []any, map[string]any) +// so this is mainly defensive. +func normalize(v any) any { + switch vv := v.(type) { + case map[string]any: + for k, val := range vv { + vv[k] = normalize(val) + } + return vv + case []any: + for i, val := range vv { + vv[i] = normalize(val) + } + return vv + default: + return v + } +} + +var fuzzSeeds = [][]byte{ + []byte(`{ +"object": { + "slice": [ + 1, + 2.0, + "3", + [4], + {5: {}} + ] +}, +"slice": [[]], +"string": ":)", +"int": 1e5, +"float": 3e-9" +}`), + []byte(`null`), + []byte(`true`), + []byte(`false`), + []byte(`0`), + []byte(`""`), + []byte(`"\u0000"`), + []byte(`"\uD834\uDD1E"`), + []byte(`{}`), + []byte(`[]`), + []byte(`{"a":1,"b":[2,3],"c":{"d":true}}`), + []byte(`[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]`), + []byte(`"\/"`), + []byte(`-1.23e+45`), + []byte(`{"key": "value", "num": 42, "bool": true, "null": null}`), + []byte(`[1, 2, 3, "hello", null, true, false, 1.5e10]`), + []byte(`"hello\nworld\t"`), + []byte(`{"escaped":"quote\"inside"}`), + []byte(`1e308`), + []byte(`-1e308`), + []byte(`5e-324`), +} diff --git a/aws-protocols/internal/json/internal/stdlib/stdlib.go b/aws-protocols/internal/json/internal/stdlib/stdlib.go index 5d1a49ca5..fa84c43d8 100644 --- a/aws-protocols/internal/json/internal/stdlib/stdlib.go +++ b/aws-protocols/internal/json/internal/stdlib/stdlib.go @@ -39,6 +39,7 @@ func Getu4(s []byte) rune { return r } + // UnquoteBytes is copied from Go stdlib. func UnquoteBytes(s []byte) (t []byte, ok bool) { if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { diff --git a/aws-protocols/internal/json/json.test b/aws-protocols/internal/json/json.test new file mode 100755 index 0000000000000000000000000000000000000000..f3ac2044251a5692c268ad239e7c3fb7eb26e495 GIT binary patch literal 5895026 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 z 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 z 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%0Aq62w93uTu9zQiIJlu?@NB31%K!B$;QeKZmL2D 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�%GH}q4qYYjwBhIp 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

A=H&(1$LWYq@pkM$h$ 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

Mq5 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 z z2H~H1{z%IA~p ziG1#_SDN(+>XlTpV(OK!TV@W<*yBH^F&CJw0;cdJuVyL3K8juL)h&(VH!B8b?kTb| zYh^>IZmBjBTesxd6JG7gf;ykAISyW?ZfP#>Me8xqIqGZGQVma!AqOy=d}<+l!FLZ# z2u5CH@6KZ1Ka*(hT|KtGytmA|$K7+|uOvJ5le(WCDZVi)aF5v-x9aEGZkmu0DUeQA}^x)Mo z)tj>f@^76wrhPU(hWFl7@IxKbuk>CYaH!Y+EzY(EsCP)Re+Tb+e!OyBt;m1nc~s;4AT>?$edJ%fjbHqZscFgyKSO?G zFEvd=@JlMKA>j@_^K{juMk}Pehd&l|i_uj%=&D?F7509u70JueZ^;co`^taG&u*5^ zdOZ+ItcPRcMTv4*{l!^Kt<$6IExNWD{p4Tk5!PyIo%;HGnsA}orJS(NQ+j!D!MT^b z?tA2Qe@kBX_u$^2llzc$cAAUdETWg}&n6dTe zC-S#)@V8vQ57@*T^0y8j>YnV;eP2$?ZB<>b^7kQn z%B_xm)~kl4(+98#gPk6A1;6X zTs5tO;?~{Fnmt<}>fgBfK{2C`b7S>G8+MXkH^kI}t#ssj8#bG%1!Lb#Eto6M)Pu1F zp7VPD9Kxnf8%FP+DHd{#TCftg7VKJbm2U5!Jo@;wU5k8BEm*uI!5?YaKf_f!XVi2CMxn7fVNi%yEj+1*2Jytx;B5%s@}z1zyD9l3@6 zYAdem>b9(K2Xgor<4;SbkDu!IQ?qK5S6tsUJ&;kG@;`&NXi0Uj^_J7er&YNt9}Jq? zqrEiMZ?O(jzvWwpaANAWm`8O}r+IyRc)y*sG_VHMI-B2~6RW@Df4c)+Uq1M8hx+u z)1|p0^vMO!a5irFbcuUWQa^a^Bj2WPru=M?FE zezTX}gtEORmay(I0sAQO_CLU*A9uG)L-=`9{*=I@Vc63((X=kto=+XqjlgSsXa2trn2S;~YcD|GRs4)|49iy-m^*+uhq~W)QmJ9)v(8@z?MchO z*){K%4%|B5mvy3h@&$ZmCR0N!$l|U@m=S#|7+qV1Am~qYP?~EVuUP1hJmW zfvucJ`|p>XUhl+z3-;C<>VxCSP)~jroiEurB5GSVAp5kgqVuc;_xk9;*$dGEvSGBf zz}u&{tw8Ow|4!TzoL}_s&bp*CbLiL62`^p5`xBs*8?+zriP?{-^q;LlmLgBvCD&ND z=AF2FY*(G3)APGT({<3pfkG$Ou#?w1SF)daYOm*v>^s48ANfx8zVKiwV9xWQpPlz| zpDA;XMiw=j(jUUoDdFEbK}5a0Y?SC zzSi17U9#yD#MwX7OY0Xwd&2T+_J#As$m4m?TV7gTt;t9HntGh&H+8Mw=j@C63C&_( zjv@m)kxdu!yGeYW%HCWuiSxB{19m5J=qPgMlI7#N#{VntEuYvW+zSUI-E|j1^P_%9 zUlrzBgPnUGcJPxw3EJbp^@Yoqc0K-pGv5;CdmUvziube+VRG2x=(T@gZh-HO?mGXU zB$LK=>5QJKQ>m%!`Z#@hm+XrN1XBVS21F0o%>PH|fjrUwE_&E%qQQ3R%y|dxY-VbK)2k~U{ zWbtJ3WbmZ(r16MeQ+Sej`jUTll=PN=I}*sh8Rl$eZ}~Tam}p<}ZwC1GvJ^>!YI9x8z6Br|Mcp6x#QSm?^fHy)N;bb78yHd>x+L_Q^yNj}*a z>_axHH+Ik5$VmJ;k0+)hFJxc%Z3qASQTd2$Zqr=IN7r3 zLT?)V3gbro8#jMrbFRW4?`$UirTC`Kb}P2280T7KbbPM94tZ1$Z_CD}zjUY#UHdBf z#)(#vUo_$69(r;hQAP;g8w&(=v?ftgZ0mi{D z+@>1mdFVT1TVcCijGc89n`8$2DPEjZZrPpKpySH}*kwI-UL|?$EY2o8ZE<#$zia=} zEjU7^XFC3`v*+&F@LsQD&yH_fabee(O_u%aiV0mE(DYdR1%nUu>8K>uB7A@=oo&}$ zAg_Y2!GBh+I>Xe+w_{%z+`GJ^bC}|5+2|b=*t^&nvd7zz-yUD-?kCyhvdNWauE!p) zr{6?tMKH2!W6m;nxD=S8^r< z*`#}&Tj2jze5bbg_))~78j*1g{5~xZjI8CiZ7J#K+GSm1tn}Juqw5vV&qgL>7$04^ zSH&R2U(ye|)O!FK(SUqV?p2?eS`OZ8Lr%8Ol|Du;5i7_<9?9ob-=GTanX5()%~-L5 zxDYv3biVXX1G4KFF|jL?xl7K4r+me-u4w@W9^DZx{V%<*oM{{L$xgw?4z&vw=2Om8 z@5^7(-tWgAkdDt_JlPV;Gh9Htsg(K0u2|l609hm-aW}qqJ+i}_e;V)_d@%o*===^I zG`C{6F?mkLk=~RI;>i0eyX2Q#hYa`z&r+T$o+Uhsd3y1*FTeSppNr#bH@~U=)A9az z2e|z>_uL+OILnG`lwPFwLalm)r(2O^`YhC>B-f_VW1)>63$0d~>9Md3U8y){+Q^-c zC|>WtU1s3E82z@##4*NpX|I>_XfF?b+->469qIIPxg_(gqH|`vRdhw>TVn$m_jfQC za@h1vP`+twK)n;v!}noFW$?N$y}Sb4XSn+6BJ|X|CSJ3=YpxToF@25h=gij8Y%8MJ zhvEZE$pLp}6CXggj4yHGHR^Al1#UAF;n$U!cgN!vnfVOA^Wqzs*uMVwhVtdj@JefC zPu!si{)x#Q!b1b;buibTJA`LEJGBW~YKHDxCPA~6&@?ovUI(f{Rv&>$%P;6U_!2sV zJxP=cEcW*@NHDI(yf@kHWx!rUdl?Xa7)R{khL?hjeLc2+riq)Gz6J2=glG(EMb}ME z49fJdsAez5@%@SHMJM(}fW4SB!Lrq}@1hCxZv~%zJ9i78Kf%+B=G{FxC&8Zl#7FC% zeJlCbK`#LR{2Zc^Xu)<7F609=!>{*RDYcm?@~-t{3w(IkbwPi77Qhhkp1vuBG{hx>_cRp2cv972hJWJ&&XQYg3zR~1=(X`3j&|| zz8PB(nHO&hrpYhD#`J8#+sM;~p>Mw}xFL>C{4_HHdKu1h2G8kuCDYrugqZ3mU>}X%Qp{`-F%jLhvX~g9f3A1H>tJubwU)X6 zVulNUTO9mHcR2RoZuG6cpNo39wA^AvrU9dPw0(0Rlz*pX=ieFNN#aSi)x)SEJDvMN zGB~rAX?L$pDy|Er+FLJK#%C9z=&kk8kqBNO~eEnpe1wOlrbBzNB(6K`BUhr zzV=G)&YEWW8LQ^1Aj7f?^8)mSPO`@oB)hnBdZNU{y@ugijD@_VCs^m-Grws2jkoqBJntr+=8UXT7!nVr6| z%-#>*ZUSFv^}*sIo{~UX^+>&L<~7ewEAy=C`8{5EwRZku@UF?BhtJTXnmxRQ|3G>T;~6&!{ImYD6nK679fJ?#clL){XGZ2n zpB(`1DEAiX+#>K?gq_iTObgpMy9a%|h|j!0<6B!lWNeF^v9;&Or|esVdI+z5lm8)q zL$>Ya%dAJ1bWP8U&9ipv`|75f!%Id=S1>mDdOWLnkvMY!I!80c7`_E8Tl~PA;}_`t zMXQ3US$Jd#d5gjHe=oczm|6R=#xB>2K-UUJZ_FR=YzwW;=gUuo-y z)1J1x?G>5!vi9Y#4!P~vwm@xk(8g~s_C7M@kTWaj37?9C*z>gCp^GoPI@*)dy}4FARnvic8Gj{?GOO?c(QEGoGva z>}4ESlGI|PsL6oGo-6;%#Bd_muuJ>cs z?HYTD_QU@t?f=YffAA&R|HdWSUt!{d?D=uM=nmCdq^cM@K2}0@5?j@~&#Dyf+vm^b zNAvQm)6=ZabH-O_*O{+Mhn)Qvoq5)1S$k|e+I20W@oLU^)w@R4_T5#PLf5Bw@4UZs z@{$YZA87EG)_06-H8Gp+uf9-g?&-|MB^O3NK(2X@e&1pAls{U{`fc^4w(}8%b!`>l zREeK+l&tVM;8zbKbF5PQM(zh7H@e&8A(hqo_x>7qR};ceyAC5!s!@|SY< z-gr2KPopz)h0Dd@avQkJqn^ocC4N+A^iH`=zi{1aK>0e zeu!<}u_gSj{Pn`2S~qkZwVi*vOV2(zg|@NrZRRYZo5It+cdjMLZ~kxlR9j*EwI&YS zP2iWvCaRufCA9_a-(&Fi@(Pg?;L98AH}7cP7rMy)JhvD-bvw_O6&ri<+(K-cxnDD_ zl5@1D#=)8XUR0A;X_*?+q2Y31*iW5UdD+PD7=D`u4t)55w(YQ@;Ds8p>%+m1(QdwV zquyUgKj^d5xJ*3LcOTm5zGP3lBU|dy zLViSL9}n3&+s5gX@K(WWeL7q|CL0@zzrF&0X&;#S0r*?%(Y*C@(m%ZoR%LGke!7Rp z76E@bJSKd(I1!IkJ3N*|KZwV?zc29cShd4rdJcN_@zy8cEg#}mPEDTMZoJqZiK9!z zi)t?pJT8v^z2JT0Gvv~^oB7r;ew(%p&^Q*%i)b9u86k9rbVDpS&GFxD-8esV9^Qkm zk$tZ|5zYpKv)uC;C6E8N^}#{LpQ{gEpL%h9VEYGIf8!qx_kTZDVO0wL|D=srMX2&O z9(#GF$v&@8^}xSv>VIBg@6+Jy1@Qe7WN9C^=Ky;A5Ou$&tjvit`_Fc{sf22bhh*T4SgRL={&;sxAlE^r1KrVAJzBEBArRTcj^1(k^X7Exndhcg^XcPr8lQQ5*6`^k)f3A63fP+7f1fy>vxeq}GCPQQVs)G&4xe_6 zo)am>2bT_2jArZ9f@u2d=UU3p%fj`!r@eeVCt}z62Fy90qQi$h^W7?Ve*wH->#U<` zvfp)mU!8raS}yMyoG0azz*{rKTj-O%y{DB=-Z?#T?AaNd)j5+hw(L4&+mhPUy~m$^m@`dB+U@;J?YT4@$v&V?yDrUKdJWWm5`5h>=tqL5EEA~Mb90yt_UAkUU!pX>>B1i@SmM|5<9*-313Hm`za6X zD}Y_+4ZE=GbM$4_>D4uMyoH|FqZrnWZ50={`D=fnXX?_^Gi%X((-z)nbc5T5=zFHd zIZx26XUpynpIC)FqHp8(Ae(pd{V3-!H;|)iD2&vBbH&`6OK|&KOB^???UUge{GPh(1-3=m7BO3eBO!PGS8i|pEJKVeJ;GT)c$>SQ(pLv>q8OUUon4rCU#q>uP*&5 z@`#1ySBFo~xAu|ULd{Vb`HzP*;`8W*Ip~GCjG-o!NusxG zKDYaI^|g5X=x4Js{Xh=Cxbi&7=!>lX5JCrpkN1}ywe@}4de_s&7^`uwpR3mL*YBr| zy!_DK3a$n#ufE#;n>?%Xn0dx-<%L3(fSHupC3q} zs~3{DR?HALYd5rpLu)w#oS|M2y=riO-Lq(IHs9`^+uT`d>$y!HoTPMydnveNTsHqZ z`D>$tvir^JeB>T@|9Aub&ju^g2!C7*7M*RYTB2^|l6$Bl)fu#D&c8WMea#88het_t z>w7M|_FnU!9SGiBTf2|kICQup7yevZyKh$Hb!R*#-)HEX)n_r})yRjFtFd{<)-{*A za5y$*uKL2poba@_!r&&`-WG6S+C%q99$!ZHXzh+@tFzRl8x*7InWgBSMs$yb?ui>e zM*bc3K+w_)$C)(_{Tw*1bKp4Y;XjvOCgtR}6eB3VRW<;>>^tY;-HwyqcKab0=gn~q z_u?2^*x?yJj%z>r(1%Ya*X13@KDT{m9OpmtL43|0fZt_LLj%Aset)sZ9fNJhpLF|h z$Kcr8nEG<5`Koy$01bfM)O*Ed}l9X#xH z@GuZPA2j&Q!qaBv(|pmt?UNb1?d5KpP9F6MXS^pJ9&!2OV(oq5Kl;Nu&utGF<2827 z^sobGye3q6F@I%tj=#{DpY=ih|MD5)%y<72c;C!*jb~iaAAjYHiS=&%{0A3bJvsPS z?mB_)6rZ~~?}u$J58AW{xP6EROKd$Ga_EsLVI6}09_*WaYKjSpN2D7n(LU;ltG3GBLc6e3h*W z?A(e)d&=GZcaQH2QsjQJ>YNFBr0 zZ>r00+VmuU*YewokF4Lca9z`;X=7h9?N-yCd>mK4igN6gANcai4s3GaZ2*?Fyc6?} zA#`Yb^dR+_HXfb%7>8$PL3@Z?MUwt%UJa5}?zd1+6ou6^ziAkl_<61*2+ivF~pd~iT)eoHQXXfIjL+8oYT90?& zqi&_2Jo?nSC*qzcRHs-gVWDdLv-*Gw5ssCcV zJApos6V z{Et`>atLo~Ph4pH*(5Q$e3d-<{!ixl^mhe$Vq!XS0-;Pb@up1+I~I>K=6K1sc72Mu zo`ilsZSVV4b=vyj6vm_2a5BHiZ@!P3i5;enA&{Pp%uKlV_Oly$dv_}y>h|ZicUjcV zseW!$pjmZrosX~kHMt|kDjzBjU&_xoG{-3)xHQ@b4YVeqdp_f#rp>#iS!2`Q>b3MK zdb=@!8|(k-(;hv6f3wn~pY5|)uIihgbLUlq;AIE0KNGn=SXj{fvK46F)@B{nI`0`{ z_iw6lWQci!J%Lf7NCWFUHbOgnDz1Hi_w`I3zl-l|JkB~R*_c6_+X@5CZd;PY+rdqM z`GOtj72z!a|0q|pqip{s{m#9^;y>|)+MVzz)@U$4erWmFn`Zh385j?OmlquvwN|p= zYQYsuAA`RaQ*-qSJ&;}>|hz|>|Gq^b3q7bAYfm&kO$kF2*f^;SVXC#r4WFQk6!mE{u*ylOvz@1V8| zX+t%UE^MW~U>g3Mr*UeYxCg#F25*a}^9~>#NSKd?=E7+ zUfzF_T&rYjEc{(Y-MH>yBY_b+4G*hcwF@3IvP2vBJ{i^9`3gfO|1TMqJk>ffT*ElV zg2z@zhIx*9VPCo%c(E;+X~Y9r9b?mbFMa=r>jkcV;o8Hso6F@@`6-`tc+|W8#LRzD z2j|VtY$XSiH&`_a{`9TL)pT+!tv|C?#-Z^zaM*xOUc`6loD;w_7I>#GUvkqr_yS$` z^%OM#l6mnGay~-urlz)TnogXrv!DbMbslDZO?pAk+PrJqe$C;z`mDmVoyA2SxR4GL zF4PA8vj-RU`+vu{HHYK2A^2th-@cT!avC}Wd$^~Zd;srO3b$4H)|n=3NE~{rO&>B+ z&NzL@hirk~SMAVj##}=?lBpVG!i7cpRPV|6o=@Lj;`+-R+#mjwtsl1YjB8R==j+Dm zy64(6?fK-L59J))N^rlY)Ux*aMp1W^Z>{tJi|=Z_gKxo|hg~Zlwsw<`XG=#}l{P+Y zUlJVeO*VvfiJtnbYh1Glx|qDeHefgP6wpfb0Wox!_g(2Z@y=EU{3HEAw+zZ!79lO!fj(__# zXAJV64>JxIew{h)weP}r5Ipw^&x|d|dYU5kR~iH!YkfuadC0^h-=OwMtZzyCizZ+D z&6!6^ubN}*>Iv{jUT(yRd+$8^%((VV&-g-*R_9rJ?t=G+4hvPD^jV+P{v5J5J9rM> z*`x2OOA;Mr6YO!?_R#xX{>JS0tng&|J2v0iTk0EBuR8f2U?{$7c6-`iJh{PFn0|`+ z?7{HxP+xKToxZ{C-{w5gQqKK(W&Z=4nuio`JZ=>?KaI{kfezJL0%iX1(L$Te<@23cif-=4|9fhXwYv};u-}L{6k*O0PhN zQAb(G-$7sKEqwylYt+AgT{ZPZ@MYof2R9W$|5*X+#!hU4|B+BcHq)&MQH|E7&smRi zzBT?|DB~+@+?y~wo>$+-n2g?lXMNP*S-eMo?4{(3#EbKo+r}Rc`XjIX338@)c_s;N z(I&a>WJ{Pv9ZLS>7tS%)4Fu0${CQrXxq zk9@Bs2A*P!1AA_G#ZP;6Z~o6Cb@0~~@Rj7ZMc_*~(!3wC^_j)s=nV6Z!qGx-lmL&5 z!P8>l30rRCN%fYc(PzPv+K_zMJZIyjKhF+~yRs7a(Yi+bc)Gl8O8BQ&IsOVbjTKw( z$?o_ae8!4H?>*3cU2-8|rXdC??(f!^DPj+)iB ze$!F>p)K^Q{TDmnG40!tpzhuFpYx;tsCpQl7YwSm?SO|m;gi5%o7YrZvKC%rjwACW zcx^nqRsgR_7P$Y}az4>xd!_%V-!{I=gO1(244zZlHow_)a`nwBhc?>Fz?HX%sh!Wp zUy30ngX1||xB7~kkD=$~UoV1Jg4iF)its+3PunIc7ATG0`B~ya`R|UrEN2W4Foq;! z7$&xZZ@qm|KKkeaHYMuJ&qceVczr7Xg zk?>p2Ui{v#KSLufqb@$CYqH)z(f%cjB&vjNo z^HOZb1YkRgt{Q@VS3SMfKFC*2F0eAnb-e=pI+A&PH6{8k`>pkjirH=ZBpy~=?u(u+ zhliUP-^1|G0{D17JTwwlZt`crzIN4S3c2-nKjLR*dF1@k$aI>%b0l%_8rrSd;gw8IRWBTbId(jtu{f zeJQAmdv2Pk&&x}P9NyfDKNlOS^W3fsFMo{n4#0|U^2jsQgTp6+Ln9~HNA3i8J%Jpa z0G}s-V+-q&whduUh;c@r2}QoocRN31&-ZBVrwjPHi5s=9JI;L)&$B9yAfM@B>U))IE z(R1I+o6W2>p)GW9D5J9yTwCeY@2^5bZ++&pAn`DA=F*}ZI+R0)h0w&-33gsj_Tx6% z&z--}+Es6R;^_nE9O;?qT&|tGr?S3xcF%Ux8UnDElh?A*Isu8{=_ossIl+WS6X`(qPrKPACvXyF=V;p9@%8-z{g@!c+RmY&$%|G z8=LY7bEah#woRFFxwS{SNH#ykyRFEf>O5n#7pFbN)6;LV9$!4%+LNNa_1|R=zhTyH ztrw4B-&C8W{B07Qw$|7otxGD1K8p-1CtJrpv(Thn^-vdtl{}`o*%>Wc8#r4?ozkF%=%cH4*T?{RR`(+ov zJ$22c(7hD8mqPa>JXYt>-thXJ#M_r!nX%#l-q~g~zV#KohcB$PdGbm8!>J!{{Cs#Z zaj>5GbAI>wo8octO1ad`9ys3ol;)pm2U}-+;9#s3`o_3dA9}0R3e-#X#ubJt_kGv5GN$jZ+xPb) z!}w#Bt=wP4chNw0DMj8b9~+|axqN7k(akY{yW5bXx!^^8;=}E?`>bsJzO`~8eYEZ- zZsFdzVWE4+1IIqAz`%j;TzS-iV?1!^ew@C?abNJQ;d_UF)vf@%sWwvRvV3&q#b6Q4 zjb{DAknm;5gle_O%^4rZsaO51_MVh}`8;#Q`p&wTjP|JD9#`X|rK~YE_4~0BTW)T{ zzn>M#tc5mX1~JFNGo9$am&yCSjK6oNEEL&?u4!kSKN+rCz@g#WM~5QHHz_Vj5U)Lj zf7`YE_M2WI7p6HtQ>P<+OMEsCJ>8@T3B;$H1{T`b{1XAJaYkgfP;E-(%Ma?Ae|v=W7(8Fj^9i2M)iY)fUG$<0gW}Mve)wRn z@;lI=RC>%K>%$!WR!#G4c--ahSK%LTeVQd1^6+G8aN}E|4L;K2vNO^{Taj6tXU&}D zbDFa(E4|RN{?J2jjeF>s-(W}fc3j@DD=^He)U&RRwfWo5M<@p%9dv98au;NN@*?_Y zp!iX9P)TGWfjt#GGydg#i((e_Bi*35Y#26VOn`g?ydZifD}3Pw^il(QDTXeN!+Qz% z^yJVBEfvJzb&ZALWK(`bJ|wcWTd^^=jkzIWcX%Pu5{SfPJD&4}*N`hp&|VT57ESlg z`yfqkfu_jZo8>&emgjS6s&*A;$Q~-@vT@^yAE{*)ezplePTf;({HXb(2k~dU`Hs#c zd(<+{ql`1ochRW{+a)^AVE!5!J}z7tJn`G__qcTlPAu=xWt~HpPLDq1jE}=rdF&;A zo%-y5K7`gVq$>QC+BdBTpFP5U!?Tgk2KLB!5q)F(X4=D6bLnPo&+yHo*Pt&dYIu+M z%7IIEE;gTi6UgyD&HCUe%O~F?TJ&PmxQhpmj`g9PXY-{Hm%Y7>X^`#AQWP`+ky zUw>L#(7(UGqrVPMKTndYT1FoemXE*b$-#QRAiYic5&LGtEZsmGK?Cz6;`Q6r2KGg8 zb~?*viIff*XM5YcLVwN{m5$px$9ZLy_5O-SMFVQwlHu@w6KV4I9EAWHP;&b z-S+D={_%3^LV{NXn}tK^&j7LK6IQTYIj-0N_7K3=c;XM(NN}z_R+Qi9qY2|YMxJ~h zKJU07d!l^Yx3U|ZTziFW=k)g)@}K%$a|E^D@vYnn{ize-LRckR`_mCBvej_|E)(-bbFT+|1MeM{eyqj5#+`e| z=wEYHRqVAkZfol%^8(wr}%&jLL6vehU^LIA?3D*nBlM2_$b?be6g!FaLEe*^^*~3LS&su1poTB54 zpAo$7J?%fAW?ggr5AWa9#XHC0$4>NW>u~FbcknLsV~ay;M1yZ|`dXWw>0)x1-!8Nc zF9BD!Tn;AJGl<$^)flrEuXMLFhR1VY-bZ~xA@3LZ*fS7(7J^T^jY8^42GLIT-M?fH zbFaKfPwYTXh(<;~0N;(mJ$hpYwPCuqma*G?yE>ym^#?}YSA5*jQNSjd7mqy2ICkdS zdgGwwYkn!;m3RAnZ~v)^S=43c(noo&!sz52Shb(K>SDj_?>`3JZ|&Qg+@_=M{d>;) z|3ICA*Y>BjTb1&2WJ^jWXKhJpdx-n2_wnw3=j0<_9rwX}WD)kT=nCe9HlUxH*%$FC z_PBr!-v^hj|Eih;l1L+J1Nf@k@5Ogb)a5z8PiWULelvUgV=JZ&$2Oto?AS$l$3f9| zuC{ZIJ?Noh=pEZe4vNa3#0HZ88kD|uf72%IyM%u=FNJ^S;@$0e@u*u~0pBO7q3z`CWN| zg1oaY97Wc&=eg?-B*=lfHHFfVH(hBx-bt=NI>pXU>dXhr#QsZ}6TH}aUndtb(0eKQ zP~;~P^~`It_fZe9_4W>79oNu^?KclG>rM21`SaDr&yruL+^l>aI~FaC{`fL-Ex?xo zu0Fq_&L1B3d-9W7r`Is}lIYohssTy8$LjL2lB!feuo|58P>hQgXm6bWKz7h zj=5LCh0i&{o98UY{%8(r5OG4s<}XHOQAbmSj+1WM!FQv3gZJ&~te8WsO`*Ae(7E5q zed(kf*lA*rdi+7+rO>WI`gqAIoT@sUo_nZW@deV_H^8(73@r^WYstL;Pv_hA`-)xU%PeK>9iLMR*tcKv>KaFbn$ka!v(TVZwSE&QJi|bCV{_uQw z-O*FvP&lZ5zt_|qsdntIXy+?juFjL5`}ss$&$Z|5NA2bpBkb{>d!9+hYAZwDZ1+wp zR4ICP!2`)Af2GdB(tfX<$VZ4PtXgB8ISyRtyFD>-UT*F~ah&KU zedPGN#+LkIGgcgZR6x=z}5H=5n*AnlC*KdV1S= zX@GXr#sp~bD*kK{b$Av%vIj$@NvDyhwkjbZcU=UW4xm=$p*&J3EKP2 zhI^1|QVrZs(VyVeew4;fM;1+f6P)z$JnILWx6)q%J^l^o;f_(`6I|M>PdPiyNycdB zR$y?)mX|ZOhn%sw?;>x9{maMQ6aZGqgLLJNVXd3+G0r^6SmnPTd~O4F!Ds3Ubziws z`S6|4$cwkX(>Sm#dZrV+Z+~L_uVq8jM*)2_kpHBJ!_<8TVd5ZbfJ@}r>2%VkWTP!4839N#b9*76X(sldPZ}b(}~BvwcwnS+p=?cqF)c~ zihes7Yb))fJ@DjFL)Rg@XU9;6Hq{Qk`p#2xaISSuqScdp_Rae=k$ZC2K2K0P*F=jP zdC@#jvMI#xtRr*w(EK#-TKrSKz}*k+LH2I9=q8|-U*3mXLUU7=%2VI`@(~joH%s!Cv8|!SIlueEC zym(T2PVvCcsAW)1a}pj*!Gj&Vr`&;jTlDc^$@@-Xz-i!Ib^M*H@x5g)u*qw)H)9Rem4ZvP z4Dx5~d=$1H{4{{a*Dry`1GMAf@gOoFUX%|uf-xnrnJIXZxwCflmCq-0Ylfe*JQ;}o z%l7jI7WHfUtoGh-!bt#ptTSWJPd`L0aR)eIA5Utf&%V&v`bFp*FlU9lOb$*uvjZGT zJ|#DK;7V&iI#&0WzkhIGP9cLGtE(%OUvTqOE?G zKWfz;U>>Ik|1LmWUF9pRr^arc>_j2{8Rr5`7Qbb4Yr*J&%UMex-wA$`PACS46R*l2 z@L4P8S-UpE=56J@q)Yr|v^QUC4SdZBbgcN7d8K(tcwMoniKEf)G5(ehIhMSa@)?q= zPYMUTE1$EI-{fm)jz+SS#P(^QCTb3v`+PijwU^ok<@6VUW9@z9>fadpM{=c4=_$9L zk@R6?zSz;5bAc6~dIvZ=Kt02D{A!nfq&t(yn)iL}N2C0K+F0uFyT*OpU%5Kep^Yc+ z`4(#_@E6Tq4)=V3f9&<)MH8jZvg^a+_b}d7R))I&%Jl(i;~K2Y43irQnB0(;CzKa* z$9I$(s#JrI_e#$kA~)2EY~RJX13Mi4+y<}oYuAIf zSDkkCZf@@C74R?-ITdf66VC4jXRF~!#;}C>HENVhJ|dr*5&X*C@-u(RbJ7zBozFHt z*ZuZe?3;=11Lv`(tRGX#9FBD8kABO1&E4$bObuxsd0ow6Y{&PM9JqCw(qpxw^3&q` zZSegD13sQiA;VRD! zFS>f(&Nbh+E5v#d)$EGrsS!1?MJ@Wu$iqFMjASjwr`GLyY4}xet^R~(%}*E^px;Bx zwH89d<;%z4JDz-QCp4_Wr+S?{q-dzN7aJN17Uk!&eDM|9RC{I|PMgX*9)fnupq*^p zLH?HQm(0FMI~|O%OR{RlCc78xlh+w)57|;Zl-e}%O1sL53peu1B&$B)()*g*7=f(H z|I&M^CE03V_BlR^ZJ%5|zRtzL{Kmb;HX^TrlW`vIKp%8?0&=EV}+b6J>=lBj?@HcH=Zo|Dx%^D|nZBuGg0Y_K#j;y4{ z<4NlI8lc@ahjz9sJGo=+&*$jcUkE;<7qhwJ{I9NmE5G2$-;7;8?nmDDY{l$ZCst18p-~y z#8JlobL?%z_<#NFtyYscWiX3a$`MlW?vLAKBf;vd;> z8*fhD)T7Vq9bVGBWEZwj@X4PhFKqOB5}a%8QXHAP1)E!hewc)gH}O}I@_}}phvd)I zOCP(2{08$vb)hQ!v(T=C-+Qk`{;RRs(4)_0!z0FKi>}fGhG+WQ>>9zxyL~zU`PRI* z>~$ya$yd#_*_XkqNuF5*4voIXSCdXu`vckR$@DP}-s{Y<+4uX-PIYC`_3vFC|G3~n zue-7zgm1T_uO)lazU<1LZL9B~2BB0s++(vl#%B3%v17CC_`}}c>($SYpFGfif3Mg+ zc=s{i-ozp3alp!y4+}xpyl9zK^@}Rj>1YqmJl5zWA6v7x;Q`;u!SF%DDBnuO?hWMj zkt_4}esTi`n7`3_hz4@~%GK+A?FXax+IVk|zLUovjI1wx+;{jWHdeLHy0@Qt0X?rg zPeYq;Wr$}Qe#w2Oog{4+FrPYY*!`RQdL~8=p7T#*`@~aP8|laPD7RGSfk~(wNPsQP%1efnx_P{1@(ZF2(BKjkSVNF9`d)Rlo5r+JjzHd9TZOcU*< z;F~qHUCNkg`^|M_e-gf3YoM}1X5gr$tyRDTeO}EO^Hv8>v~#1|4tpi`Yv*@Apq;nLI20tJfS^E5EJgH*k|jEkF%<|0k&NS=Tlsy#B(`SEdaM z?bUiq$w%UOp7jU9Rm?Y6XfPdE+OED@H_Ots&ZDeyD&oF6RCe?q3m! zJ`v;^ zN`3w`ydZuL7qX9}sk1_UDuF>b{w2C20sPt%TREN-V_G)~`WpGNZH{Q)2;EZX_a^u_ z)(l+euEa7s{{8}Stc&-MY9l=KYY{FtfCK!lM4gRy**&)(BSTHJ1wSQc+5MQDr!yw4 zg>au85GL%7foTIU;j<;`vM_0{dfA^#zK@a? zjj`6-d?WjmL=N1x1{?$YWng$17;XcG`Ya6prZH4~$oo~!`cmiJd>-|q(}_A~*1 ze_C+10OtbWyvGA4w*HcE;;$MwTj*y2{oIr7$9-=gTkd;F*y zDjYjj0{tr*_s-49N1OG(=v;3-v}&1EzodALIgSjrb|Cs_F}3HV_ByMZ zSvPb$HRhcacJ2QT{I50edz^W`vLH1eyxT~=JkI*1HQ0;ndMdkSS2ow={~S2qVJyn6 z=$~63^kB|<8u^)%ur)p8Rd3~UvTx99ujN@&i*I)Uil;KlqeM1dx&)`$}3i(t= z23wD}F?Xgm%hrb?V}_8MhK?0sV92u>p zq0i_`jOPnEVIYrMW-#xew44P`tfCdT?3@;)c9wOW^>vFNPqud?1j zwnsj>KF5H+CmCC-#>Soz6|68Gt;=Q@ylP|&iKa|Jv+paX{VOggNGk@@Q}OyA!`q^_15AS*2Pq&jT*Z97_y!;7@2RhqPn>)Iv}&Sx#>+Q` zUM1iidaZNeMECs4p_e=V(__xmyP|JTl~}DMy=H1M7O;1GK6FX3-dwu+c0RRsauyeH z`{9=c=Qo=?67{mG$t`C+Zf^y9D?{_%-l9mVf^`Gjx40k2r;?941N|>s_I-SD;axRs zItQbIahW<^d=O%=Gd^Uh%*09AwU{$UP*Z~6B!5nAo4KM1qF-p2a_(At9;?MZ(AJAu zlQ|lD)7M5p^u~+6Utn_f-J`K_yszBP!L}yWy#>-Se9~^{l*E>IO<}(xv;Ix%`>9Jq z@923KrevuNSJxC|8av(%&v%s+MP`Waz>8?7{c;q4=u^C=Pn~tqyPbVYv5~#K+Dnm` zV!JOq8J!RZ;)kz8ck!^k2gL(CYx98T%pZpb{2m@?^!Nr||A6^$ z{Fm%{>MHnP5p+_XS$etvI3{o%vVyN2QhfuL;$O4&5Zr5yJT`%RF8+f29oK(w_k#KL zAnI43*FoBV2fkjA--s{du8nYNi5)-Y?J`@(xcn-4dey-9@hq=G6ZuyP+sS`J6+9+nX@#pwd&@;}s`rYH1 z4?O452@wZw)n>ot;MwFmn(Xs^QH&7=(tBksX`HX1+V?qu)8QTVQ zWH;km0nU2ZyLls@$Ie@oPf}-JO0K098d@5Ed;)fDl4P2*wV9DUuEjRiotgj+S8hwR+$>OshI_M*}RUA#B zbA%t&d)~SKextjD&lEaE`)_pBxcIbnmp>Puz^b`)?S;`*^Ff#kSo0wptn-rAfzN#K z@nhz7Cva9$5PU9i@F|@pJPD_r#0jO~IL`hP$1;!CvgsHcq%co{yZ6pmb87jKhq*zE_l5bFw+ zD->s!ik}7cRwP-&C8yEL^Hpci$CUvPD;4;{Cfk@5kxi#B#u7>lnKh^piRDvaavt z=JX$7eTM6QDW}i+(7ssDtIzH8db0cBjziZ3<)>16Q|c>9PeN}TqxP}5$U6K5>Ktz^ z|Eay#pRYJQ37g$@Z~a-$dkmjsKf>32L)yE>zP_oIy-IFnuabRycCpu4mo+G=^TZZm zC!eIPgJnN8&m7b~XuQ9Jcl50GMPpyK=wR@L9uKR3AMc1?Bh0%!$+HKYXJ6)7@sZX6 z#lWXM&#pZe>^*yBkK{AR?(`JCyy-mG9j$@QQ;PhXi}H&$BinE3Og+t|bz@&umn<7; z*BBSmW)XZDFneG1SOM*W*IeZrWOC>0-|O9@+Q1n6Yy0l96RwT2_r?0#apzl}g^oYq zdWK7M7meg=O>$_{>anq|f4m~+{9^foZd{y1KHYPQyYL&jx93%cpp|&iwWBuQ7aE`G z6n=g;y3c3(#?P%lE+0fLivpp{&zL7Kn_xw(fMsInNj~)W_Gy*WufJA=U#s=+)_QF8 z5O`;5DB~x_4)9&=>)dnsWN)i|o*StBfAzM{`;%!uP5bNt@!BBTcjIg2i`}{0PIR)` z-LH0eXP|bU^tNl@4ZWq@#30(A?JKfjM@M<#?biqKfckh!eehgvAAWH2kosWlzHsBG z-4yu=vnCz5T|ZlMtIuogDQy_O0T0jew=JV~T-GO(Zv9n-YGXM6i}fUZvgy`dRa93t z48RZ6>kXJMl0ReeiP%@wPj%uOs#ZhwQi4ahpY2*_xxKowQRiG?U*hP0`CoflMYI20=eG>%w;x7qz7IR@Ll65h$vPiChcDBcEUN6S@nz0e3<@_iu;x^}m@G2$ zu#MFHF2$$pb>ar;1^4W{ag4dE0UYhacg7}{+dj^l-aTAy8ZIBFn*OTkubTdGOnQ2jmf3?6eb%PPk+nLExbc_#`&J8Xqi}?xOZhdeH)&o_gqr zU)TLKew~RQ3ZY{`bPT#f&*#FX+MVvF@fqs5=71Cbpzv~R;ZkfYI3afAs^hwW-?oDz z7hiQnW-e9uy8lx6S`EH_a&deeA2tBKegeK$UjkqFZC-r6bICRwd?9B8;%gcBk`CP2 zA78&fb_c{4a^JlZTrK0e8Jsi~8GPL@d({&X z@4a>W{jOrVYa*iI!XULvbE*w|ibpzbuFiaixTQ_!;X%82JMx$;3MbR{`u!N+<9tu> zTaw>Wb-{1|80|Sl<(2GpLAMY)$iIJxy7I&u=#DNc6FXutCxcBnz$Z5FJo^(Y);?g5 z@%O{vwVCHO@H_R7nzQfZ8SUG`ey`E^8-gPkP9C67#uqyh%8>7FiLFB4C_mAKo;c)r z<}vuOy`+#Q8TL?zA--=z&mLI+4iBKfM{EqUZgRZl7WB)RRTq)jN zH^x3s&|YJ#yn)W&4DBlO5eGm=;Vp%ZSP$=X+~cbxf1QaBfp^duN10z=gU=-0bdi0jmsn%DR z4xy{M>a0x9Tk~Z!ojot7QT|$rUv5S7qwF4PzBGDi zUTOh)cow#8v(>kU+H40u>Q}L5$1HTrW^$PhUfjL<7BQXz@G?G#odqvGaDmOJlz(aF zd7B2Ax$B#YUfHx{XW`}^WJ0=+`2af&={M zw^^TsJpAyKL+BR_xGORp+Y^TXdGuA$RQ0nFJ zU^INu_k-{T@0Y^syx(Aa%BD@qM@b*3=0)f3c3Fh&}{9^}Gv{$>~+uuytRL zPFTi!4;M#ff}c)s(}~{32io0vU|Oa4Q#x7kwSKc>aN=ith4DN;9^2nrrLL(1+b>%89^m%|{XQ(xTLbRr>ieKb?0}xN_lVb;*mzA|_{Zq(&8$gW zRE0k0_rKxqM$Y3()=aCcDYT{KeUcQ`bTsUz7#vH&CVG(@8w4)ec&DTa2##Zq!k@K zvuzz~mwaY_JQK53Sf&=yo+k)ICpq*`9G0q}-LJKTLI`tJwQNsWIj z$hbXyyZNazHTfn!eD!n4Z%skJ_%QiAwSRAeha=3dK0;j|v}{=DTlrJ$PQ&NetHAl& zqTT|#Wor4*)$_CukJiiRoh>}`Eq(u~UEA02uXg=k%Kyz>8kg=9JJ0){`c6%s-q+YP zH={Z}d_LN{Pk!N^_zl+XanPs{UbWhWh6|{jnn3N;E7VRsiQgD|1DV411i;Y_?5J#8 zCv|TJsm)VuRR#Eyzp{w>uZ7G>#CRr-jZeHSJ`C-e3BC_fBWGey?kndbUFp`yw!&Ov}wjdzEW*Xh8|ns<1O&%YA4~TR#K)6!`1ncI^ob@(`ZjToVhre!LjT34 z;1dv2h!5_E4-STgh7Z996XAm%r-m+m0Qj3cHFWq6e}1>Ap?k!xp}R6-YSPdJqu)EP z8anQ)hHfPK5qi`2Vql)O`>Jq-_IaT#w}!6ii%tz)O@Wd7LB67mx1PA~$gSn9KVN>w z*<;qA<_6X{uSS!Y^oYUp$ZLm;{w7&Ip7XYD&*9SCK9&Zov^YHR6Nb7k>TU*%S6 z9#tDjjBNI=_FG!>L0$d^+lJLD$3h?aTRHz$&Q{nD9|`A14>|DZchOz+S9|&%lYX8G zexYZ%hFWaHql-em%o4hAE@muVt ziO$!1TiyCfCM3$dyyyDY=T3nsXEF`9IkPp11SAc7F-(Nhf&wH+AN- z7z_Tm;4$*x)MT!QhhjVV?QZzE2|YaDuKiBVM@Inj2Wl39yOHsxc+S+U?F4T8C45RQ}g#(^|P6N>gZ<`{mgOj2d*!P zKb|xA+e|-o^s_44kNcjXJ$z|uf3LUkUW!kcOT!U@p#>Off#DutnB~9#&E2;8)knHC z^eCkumDHtLxFP%Jtm#hI#{PqKcU@37gQV2QPIBI`faHCl)Bmsi~Mdt`JR zI${SpVku{V9<;6tcZ{aq*3Es?1f2ai{L!Od=&v7otip|x&<~T)53em>arRZKsJRyX zU?IC*^gjswAlK{uyb58F3 z#w1f8l|8>Jh8|C%v$3CM&7lkTP3*_RoZy}q`bKLpV%UO&Y{3nojN)JUNb*7NFQI0N z-{bcD;3IC$R0S~GAaMZT{CSzpI_I4Kj? zX&zMTsqET(JLhdbueP%Eb!!5g-<*87Vw4}aIAN}Tp!%N`IrR$h3iL&(W%iI4E<(ut ze&!ziSBH+MW`B{f$95jl)c>2mL%X!5E)FdcmxIevM`u?!IvaW`y9u$bX5!@6*}83wfN{+v|_Sc*kw~#`EV} z{LD$%``h^=TkylImp&1e-6Id#rwaMd%&5CuZXFC&ejh#B9{a4&rgSGUl@BX*Y4Mj8$#$h%Ni2C z6Ik~@^Y?G9VgErN`wzxhb7#+geefM}Q-+4^f(aV_mlqxr-(>5B#Gke7y}1Y-IF@zF zFVn|*`e?NR_3~lFSJ`pfH9+5_2CJW3dF9`O2kA`lj{dp$n4J?}#U=<=jp4ps&EOz` zZ!4bHnsN1AEk7Lp`Da^l;weu|7k2wZ?!Ei@nR>&V@k-a!A*ULXJ2yCnct&w*Zftz_ zAmmf>h!26!hCRrsd-9C?Yc34XzwdnFCTBlE_P_VmH>sW}i7oHETXh`4^c3d6*O5<8 zEwk?*3UWpbHdcEL;`4)l5F$QEt`O^pJOF2c=p^8tss0~e)=0!S2``KeHKk^!REv*NP(MFtiOX>F^`t9QV zQea)qp7{BeuRXyvfpv%z*c)H_pIAeJ&0X|4k$WBbjrED$z~bk=nUfhqyV7N}n*>hz zunFRm6z|@DZ`s+;qYsojxD%e!^Dxc`bqoQq9^N@K!}1=rF-#^SoV; zl9dVj?;gbia9(orr2QCRv57xAnE> z`F!mYFFd$WXC_P>QrN6{>B&|R>*###AZ0=i%rqNXePT^^_yeUfJ0}t%PN4@RTsN{*m&~b>UR+`Ic7h|MLL9{TIJoipPgv zgvaN2c-*x=s;lyzM;OD0OyXw=H*0`(5qaM+j6-=Gt-rRY}z13k(z(+;f8XF8Q9%Cw^8rx_k^c zjLqkF_)#+OQQ*@2j^;X)BRh#493polIrymWYu;0R?xTM91o6X3Ye@4O>|jwvIXx^hIJ*Iopjhq*5dEBc~byksqT& zV!)*F$TsJpmz2|qW6!k)vh3c4XX6X_8{3`$$H}>jyTmyo4jLpmBQ6dd63`)DoF9p| zV7u@0ZI03QcD1dx=I}fEN%<$g7<_n_l`5Xyd3_OEf-J6pyh6O zQSzaAtOq!oexDB=fXq?vn7)Ni>E09g9NT?)?d8^x_Gw((-)myr{$_Jt)&zg#*!B65 zSIU8feuQ)JipjUqRw4eh+R_<+6})IaLv~KW_jXo>C`6c9dG4vnm z(BEyxzyqvt<`-=lw)Z7#WDnE^u5WRDlj}aN)m;CJ>lyW zBFA4SFtOz^)>nMM{_dR4Gc$N*X5=zQN4fgRu7B&lK1R8`m&kL~qEqmV@tG|A66g)T zt6ow#P3gR`%kA^V#4BG0fAZrBpn-D5^3i&!gMSEJWcKrA&*UQZZ7O>2!HrrQB|F8r z#3rw4cpcwl75gpOII?5Iso-HCee}beF~08TBQu{uf8O<P>AFp7JBZG3@Z|a|F21N~>8H=#b5mt^eDvkj(?JjXYP-pSTl>hl?G>5z zpxN`hSTkkLA=dsWw-<4CEwwitR-k!1@+#l$b@ZV3+~BTK?UmtYZ#T}d1O}~TQJkT< z$;1?Bg^#G(k+Jx+>o~`0V-Q_A0bL2LI<7}oqC2|rTM`xc`S>hrITLxB6=-imFD!ze zPqK$>q5?TCRX*CE-fsFwX3+_HU-e3z$Z(LDd-Ub(`%b+{6ZIy{Rb?*c-SNyzGZ(?T zf%JA{NONmRWL$0e`Ma0Tr@$$7zU@iQhi^kJs~r2jWbgOhO8K_@<|WRKRP1Tv*51?o zIof`f>jzxVaJlkQZDRSd@NshcF7F8UV;$UY^YFjxe@-%IQe@YIxbc#F&HIDao>uf; z9Ng|l#-V|++cqq=f1HQz@8Gvt*yZ5hU^C|^9A3`;C_0z95gqhV{jF04-q;~0&bYq6 zzm@9uEcAmGx&D^%LDBj0LESi`6I>a83mKF@nCoxl<6A4<+c0Wov-V3OM;Mme%a3>s zIw?j>C>DgbTsyp9upsjvhy`UM;WNd4dj6FQGmmtmTV5RzdbEd}TP=RBpO|dMsG01k z7ibT>w|--Qb!-jr%0l{D3;)@BzWAd*65Qw%^`kaTtV#Rb@L?~oZKLk4^xg?)6~~!& z`Ma8S;hXln_Z&NijHj@1N%U3+dcPihqxF|+Q??DiYF-jvSB%(1jHnu4w=eZmJ8EXy zk3J+$+U=wMXF?84nsdvI(-cd&{9FNFJ<(|5BrrJt@xMsM}#dT{}2cd{PUaEC9#Y?gUp~91VYGpU;n8+wa+d`8x(LiO0SC{Rz#gmLh zzNK`p@hjnX_uWk3BJaS*=6rUZjZZ*VBX0+hjit!O4lCGvKbQ7fT8}k2Nv12xYpy+)*7z0mepQPn--VaT#FC5 z7+s&>?9doFnO|t1H_3$4Mt`2Rarn`A+J1KCCHQyWWgq0OkF(n(~!6CZpl`lXh?Cxa8wZ_NPo`v=it4!kiJ+TrhsW}=TnJ-NxMK!E0i)d{bX~-E(V9PDi>*^DOa^7QbOb9eOm29_Z!xNc$c2cXUMPHzA&v zE~tWjJ@~iM393QZ!5P3>%d7d3d~Duzz$70`JSsiy%~83$Wy9}_E(FdvV=`lN#?&WU z=!@+0`O&}Z_bjr7-pI8bniELj6KKBJwH@;{9&Co@?o#NdPUS`13%4UBB`r?O#>cuYQGjtxW2AAF}CVk9XEKP&3|pl=Z!bR10-g zBoCY2i5~w`@I7?!(B?Pze0Xq4b1!F$mqDM!{63q@{r!5@Yn^g_mwwc|hnuTzpsu)@ zcmC7|efch5A*Ozcui~6+r#9$7p{W^oV4}5q#{}XO@hASz-{U9gopQbl7YTC3Sz6gT zrzamD44P_gS$Sc#ExN~P-^^(`ZFjI1L1QkZy*0?hNvCa}Y$Ny50c+1b?2euD3>bbA z9E*TGx6hx_XZ!~0HLs@@+QU!V6f@8A@>3wXzu&X)(=72*AYJj?dTQnDoM-GE^eQr% zK({V7@{u3ae!H=E;3xE`)>~*?9R_FYovSl8opA~F6HS9EkI(gG(yYZ0J$q_=neHl^ zzMWHj%CYZ039dgOIbHycj?&jME7PWWA^iP+Wlxy|^ET6n14_YxXxL57o7&dgko#Tq zf_{6pIO|R(|J}Kk>~HS*r=Jg>uPF@Q6|^EJ_&fn`od8EC;4k(KpkL%ecmsc(Q>)>@ zYT&H~?rPw#&K#dxojK8TYvoDaG3(o~hbP}LbszIiL9h1ZfW5A ze)`F!{THEU-wQJ3mDnKer53;i2la^-)W0I6|v1aCt=DDZP{uBKGiCBkl#tv+Uo_R zmyfger>V67PP5k5r6GBhOTk-=o=dY1^e)z-K0_X=zWnt~r#O4#6l+gU)mvtLqSlhu zv;I{0Kh1T9>s_wja{Z3$_gsJAI?I*e`ZQw~O$O437t?v)2QME*=Y5OVSHAH38$y}? z9poGcWKDQKRUgWnhNfqTecxqHr@}`r?M~1p&OUQ0 z@RmdCeZG?P9a?XK|8aVil{s?{xup$O=66Xe^KOHc`R!^e^ZPDz&k^W>f0DjidU_P| zGpZBM!(Lp)ocze>L!3R5yqopa?E9U<7wN!mcOJ3Vlq3%D&OX{;4U>^O-tAi(1gzcg zR}b*Ucs|a%3Emy9^I2&xwU4$A(3T&a1I$6@8N2Tx7urBBbTu-xio8Y_`K_c~_Z|NU z{q({&FW^tXAIbyhOfaLTm_HB?i+K=bhR{#+jA()90b-v(U5& zoIGu1I(FJLjQs)nZil|D(D&VeX*$YF)2A8ZPU!Flo2II9kDa2;78}pvhXHBI^Ksrw z@cuuIaA?{>TbpU?6u5V3+RB)ox7Yckp9SCVg6s3_`2|iBj{)Dq^q0OAO|@?<^M<_B z!TWtQrOm}IO~nh3(Fb%*KTQAhA-bMO10Q%6U0r&nw$lzUJx>3kC+9byOYM0^@OJ+7Vq0kwZ0#fE*B+7RiYD&`Mn=3w z8+Xdr_UlvVuM<}dX+DKL!e_!pa6UcP_0(2M*B?U{+{_yPKgL#g?G5wDe^eV5W0LNB zaU^wahoE~qczvV4?f$?VtDEbc3XdMJ*E)Zd^*Q}=06J&F&G$})FAlKx8|yaQe52-u zy!p{-LGl{p$H)~-YHorA_#lW!=UE?roKpT5=h)8F*gCM0~=$+>pi7l+rSTg)28dqNq- zv?iXdF+PWR?-%`^_Z!>PRTIj@cs{in+SD?x1y-hk`1TIs+fw3Bi+-n(?^1oDa$-MM z?FrBK^?Mud0;_y@<&*6-tnBR!&SR^14t>EqLUgBmGY{Ux|GgD~%HC)34W8#h=k{*T zuRI2hjPrI0@|!~$8PU;A>HWOzN@Guv*Ve7i2=*WsWvdD)Pt`2;x`tZeF$Cl3_9x>bk^3Z ze9hJ9(X%<{OZU}Tcyxed@~fgE;w|PPCWlk=S*LwqP`Hl$@Dkexg%k8EpO1Gcg})bA z(`00Os%7R{lXdyw6#Gs$+_&Z_@1=dv$f@p8UDtScUVP=m(fwp)qC$McGZfqtFei$FSwK`KQ{fivO zn=hw+x?$H0VAR}m@Iv*GF7ViuA8hUhkJL>!5904z1B056-EC#&_zIiTxA`((SY(Bd z*ISuy^S5#V)`~mME_yH&$zQST?6`jnMUv3wUy#YJ+n^CN=t4)RJ!{2;v;E{8Sh`u~ zHqdjbDGSnGH@W&rT%I^~#LrTuR5XL_fEY-lW(l>mg`co~r zWHWZNW!6T;o2<;__gOoGT@jtr&}kX;WdEAxTfn$m4?~ZZM1N_{G4$waU|j_+ zkD-fa!so}(-G5H4yN{nyby@{oWxPYm$>Sl(BkXTamd@D%Rz@ALZn`)%mn3hIpNuwnT8 z=?Bmkar9@0zl5_Y$xqd1^HT}zXl%ou_S3bWr?%)%xX6h-XR*eD`P(0g*e+tbMBfO+7AEb z&gowUtgwM8$ zde65cX*;!3wW9fH^_!$!^)XQYMsEP0^nawSd6VWBV&aYG-Mtg!zvoA%skV&g;?F~0 zr_F)d(;EFwo>#6`wXS+M1w8Ajl&6Px6+IV^po8oEi}EZY1D;>I1=QDGq9Nm{g@zBEC)<)=-0Pa>|;n$R-M}Hk9H`@$Nq>tjyhBDUxdrCTBwNJ6x zfPR09dZ6TL`bVCHYxyp*JA(`$|_OGoXY=8yg&_dT9JIS09&uO!(sSb9=>0vo*weNGxW5o0i> zB;!mlCbb!7T-u8`1s+RT3&4E(yi-m7a4-DW3qN-7eF*K?F5G`?f+H`hGny@nN7hrfpJc zfjk#&nLTb8OBXolW?VhcQgzm8UG3=2mpZ~6NbYEah#jGCf<}~aT0j!c0O_TWK(r!|ASWK2Jl6vi6-D%wN)tYPV4FRs~CYn7NfdH-)z|DiLeOAPN$b@>i| zQ#rDev~vnRDCK#X4(LL(LKheWG7E^Ts6SP zJ=-o_87>^?y&8DXyyuUeM*oVJPS;t6wxY3UaR%Mofe$19NWXt_*x46OL(?1n*VoRv zK2ZWWP<^KAGQWmTG>mZ%D;U;%2D*>G<7;P!406iuJVg%uph{&m1-xAWT;_AgUj zBgyzw+b|A(i+RS{4@bP)#k<|S`_J#@(8Sa3T&G>N=eDi5(reFb8>kq-_Dj0xvzzC8 zfW_;N{FFV`Bm*ht8ML+bIFy6LUr045c2*B_d?AbY+Uu3zbmQ9a;#Lbs7=UJbgFQxpT zCBWRsd3|&4x&2d}SAY9w@_Np@@0f>P0guaPDBq##os#=MrTs2!Yt}}!zk{5EbzIcw z+Mm}auWkeX=k&>^u0UQVap@HNXs^%J=p)L}@y?0vU(S7#o5Wh#ei!pGPwS+;uT!)j zK6m|Chn+Ds{S{+s!Jcat&XJcIPX^-|LJn{ampg`hbh0LA4C9c=ZY}Z{v6~dS1nvyr z&h0fG$q>m8cYjlYb28(Sz1J|NYZ)KoQVy5w#r7+~0k?tQN7SSnoU_(y#64fgS*$(M`nRY;ygypH@#YOfRP+58&&XgeIcXcDz!z6RcME(#tj*lx{L zo4tRZ#P88s_8O?Y#q3|8J@{&KIq$YtjW1DK&CHA1y2fp5ns06nuomR{=34rHozFgp zjQgtBC;h14G5bt9x1Ad}m+mk&iTUjpslRpf)o%}&eP#vGUcdEw06*USn{sGV@D%v0 z@9O;WQND&1YFqE!-hau84scnz9b5rV2lRdz9k=(mTv$4w&BN%MvL9_3j1LqX8vBs8 zMQhS4ytt~&wT>+We{ID6rKfy`XDYEB?R)dhy@9$n&b@cPxu^9(@_7=z;vD=_Ik~T| zuxq5*akxnjWUW-~1JybVL(A=~?T6uS&U429XeBh3R%FRxWTf`QbaK7N)t9#32Ol5Q z;CH$ky@h+Sla8XFbi(T&Fn-mnKY~1xe$g@+n&Uj%_a^#E-k(ej$zF4=@wbZ4;aP(p z)@$vzYne=p27Z2TA~h~Kuk{Er;Qh(u6M(;?mGq0;j8)n4TYakUWArCD-2T;fv(tC9 zpY_rJ=&@#teU{*tso&LbKedepafX!7*SHP(J%Zl!p7deH6!o9m*aWZDJm{P2V{YRX z`RCMkiBdb^ah{LlFF2rQcJR!AZPxzpT^u~vFx;A3&0Njn^ZDRpz#`v%eXf~l&3%;n z1Hb9pzl`tFZ3hIBzm25*%8+$Ty7PcPSmqtk!ocrY=6TV|hn$UAlabe1MxG*Z`q1Pr z<*(Gfj&ZVGZ}OE?aNa{vmKEwIp79X8&^Zyjqwm`1$0X(39qgMoGzWfr(Z4CH*3D(9caPZzG%gz8<$=Lzn4ys!~1mZ#kX(Lxfkbju4ZkN-DB5G z9ne@?e!RYrc=TV>wrhWCUHYrucgBV6d+82jvS?DgZD6h)7MhmNnP21~OUA5n@0Z*6 zl~=0ykbIG>bmL?;O#PgF8ei_(=C6F)<&{?YL6#ls3*F$~XWx%(nBXS%JPy?Tf29xi zo^qw0^KnKo_nTKe%$b+{c{Y#r->#AC(t*9s+WqAEf=}J}fPdwr2l7_toE&$sly_^- zt)+ISb?g%4jF)~=ZH|1SgWOM@6V2~o>^|^0$ zSD*U|yYlZF)n!3L@;%XS=MlzIV4d4JoxLCV_f738^_|+)~-7$2X(hPgt zYBQgb87qE~k6yUdnZGWNo4t|_EsgS8hL=+z-^VkHV>? z{x|H2O3`_!rM@SfeX!H6ce!_{@*R5ByKEq4xQwftxanPro1PPX2tB6~J!gFpdr{a| z6EDv!kTfNr?ox@Q`+6l}C|xcYMyas{~-==-+c$Do6t zWi?rYeRV$GCzs*mWA8J6Rl$=*gQs3xXx;NPbw%)BOem@3e4il$7pyQj3I-3Cvad9w z@iz%@^8#{h7O_5jeo@weVr$#)t7u>Ryr^)eIfFuR)s_aGrOdPNMcdk7LhrZpjOcL% zbc`OU+>xcqv5iy1Uyp{{fbfefQGSHpcm0a3=ns(^+9U7(`Hi(J-Uqjb zd;xRLS1WuJNAFP%#v0>mRUL+N8t)U2@{Ze{v3usThQdqAw=niL{L_JcBEC_c?0bw$ zIFqkdwVmU&tY55W@=^ag`AX(9?@EqI7D;v$u%`q(O{uM(I<^LVW2$f^oI*!2^0fTW z!Xe~SbA6Tr-pY&A`5d|9&4c)n7l+VxJuDnLY zs*9mt*_uu8(T}O$^7u~!p}N^~#+F)noz(Q5atZH+p}}X#EmS_+d(dt>vZayllZy7P z*v0uNdY`>EOE&QS9NwQfd!~8+F~*xkpBs4g63$_nZRIx28MYT)%->W>?%+)FU{%L2 zyLy;`OSvw?(ak5&-fr4kPkTAEM@*UaGCTJGkJb+P4VA-W`!@?3pQVlYjQc*Wd%0Y> zMLcY2k@dOI&pxo_*VX4ByYGS?=0RV*@Mmnge$Y{^Rnk02w0d!4D|NJq9Y^sI*@?6gOE}1=MY*o&l70AC(H*u@JC%dQAu>QS)kNi>gMbGEAJD$v0*^sb~?SSqT zlYgFhex7;$@$5UsR#MNsop&T3B%>Bq7mihog81mM=Yf9}v{6H>RCVUmpN-d>x}+~1 z@5}x_uE>CzO@HD4mVESP#-;JeS7?tduW|Ls?zCgWA8e0}F`2PBeEz!e@Elm z4_wX2supCHWN#d~7(=FhfJ}{Iw;inyCN}!|*X{T9Z+hN$P90ZMR>nC^@1G1U`2c*s z&%G4i47z(WvQ9O)+n5t~4i7)ayi{Tvybw%OQ_Ci<=Xk%B_D_3mgZ*6e7IX~Wk-t)W z5<@N;AAdRa+Z=KL;itK`l(X)g+j-}a?kQI=7eB?HH{hPRUGuVTZk(6LbspCct}?EN z^nKg!KSE|K%NWo!_p|5LY0hMSO8y=34`|#9{_aEuy$Jqld;>be)GXSJ3}2o%ux|Mp zYya|$yt?J*uxBhQ)cH94Uqk&1oeL$Jev#+q5;iTIh#- zJO`dm##FUFA3^Rte=c(h>~FCr;Sf3bYdVqf(Bv5Ljic`wxf%Ri==S$d)DP2r=Bb$f z(k;^YzDx}T{0EY2KVW}HiayvYr}meQ{5ANU%ZI*M)E|y6Y-w<70*b& zB3=@DnBUzqPu$;IO5rJfZ;SgvcFaEq-$>F2qTEyCvoZdX%*N7QzDDo3G>%!l#&M7} z#&Z6={=3I6P4(Z2M`pnz&*2wSF6y1gEWy>=FYC3x+N^AyLmKK1EbPRlo3ho3waf1( z|6h+!5W22Jj&BEN!ilaE=UGQ*ZN0N(1ilf)-}hiQ=lDj1PWTH(XP-RrXpO&MP6z&y zJn-oK{lb%#<~OYg*(Wc2bR}_IJ%?ZM8g$ZqmNgX zwVmgR3P+&N-Dmdh7Mgm37aKiJc@KGip&oIsccs5dmr}drwC>*ujyi_02bKAYLf6kJ zmo10-WUH+G)NZP4!=Ior&tlG1>!p}+Op}f08GjYy$JcYUAHDZAU|Iz1i>>(2+R&rq z3rxBHapj}Bzxd+|AAOZEXZx(H-0^BZ@h=#2R5Flp$~SHM9F+6#o}p&j%<{u^qm#UT z5itm0&dJTGqux~AX!vV1?Y6OY>f9uqp*j&ikLcny=)(0qXkC>Kc^$G@y6tvy-^^S( zXO8T|Zg`4%kl-}Djd;A?uj2h?=Arg}|H_^Hp$p`+VnT|MD{h;;+S=a^&Qv3RX!WeF zO!E4A_xB5ODD=E??bM%Bll1eZE#Q!T<$iv+x~glgV9+^+)x}*c$ia2YhjKz%=s!w6 zs-7ujZT>NIr12KDKMu>1p z4>0lY%aV4&_s;g-W%iuOCWs(2VyqJd`2I(3TR?1_+6c%`zp)V%8`r%08^5D`r*is5 zF1oPDMi4B@$=F>ztZN$M#>P|)Ea1zv^POw0aBCg4TvoFGyM}(Lclj{>w8#i_b?S!2 zd4Gm#kT6f07ycZ^eQxbI7z%dU_d6uf<1+>BwY zMs{Bix-{K8Jgb@|>W{hV%whj~2V>QFKNk2<45|&?BYGvZT@7wjTQ;|Gw1b!EmEZ+9 zOw7m`OB=d^+L*=})zHyCodvM{&)rvTXN=9n4@a=SQ+0)e!*=LIG0g}#$fcGq_R0SB z=nbRpt?n9?c|+rntFl3k=YB!%Yo*Eun~h0MEnM0_cJ ziDR?2KM74itIFqyATL^Kv19RFjfdWD19$M`(p}r~L)xbwACw<%SPtTi(~gpLH60m zZqt(b*X9$Rg3dIj>f=`WNM}#YW`9j@drJ0`>?zq*-SeEeW9`uyE8XC~yE`wu;R5!! z*Mk?H)n4Bs{E-zt_<%K{8JnU7{=l|huJgvD`F+A`S)U^H(4^)T+?tpt-=`{fnD1JD zl~d;OsrXnjaNK{x)#im;8zzU_XQ^NIvtWOld@W?YseJ() zUi(M(U<`VcpGJ10*EW>hC_j;FM|yLy}>YX$j&Sv$8Q1cN6Q)CO~3@b##wW1`(OFw{>E?+Zodl_NhnV!5BC-b~X5Gfd|iKM`&Fs)LuJ>&P?uKJ>&B3W1r{s4Nay- z&!n*YlUh%`^uhc9@0d`Lsq^%?(04m+zsS*Cc)AiAg!lEHSp#oL^ZsJ?nmp+XpJ_kM z3igPcP51w7T7Ey>f8ydE|B2a8cL#G|k0a?nsidzIes}Xv`k!u3#L+>XJ%^gT;833r zL9eT@6Pq*bI&E{zoZ4sbD33(4RWfyT8k+w5kDyg>r8+jo1_WQ?SIJ+k#}CP;d0CRS zm1}!_x_2&PJ%uLpY_ryH_@NEjv~!Q`{9e`TR6fJkku#EWIxjamCYZR2enf-%&Blve zFGkN)qFcD*LAdtRmB)+pDa7d`(c zaSP!fWgRMD&RHKmWRFG)Z59L*-|a<@HxQ@wo+qTSsIE<9gk}8Ll0k8B)8>p*`8$%M zQ{U*Wj||eYMn~K){7A1`hz_sySZDK%A^$SQ@2nY{VRq=kn_WMAlIp--C>(Nj zH?a(R-1ynSVKIF)UkJ_d?y7s(D+R71gG75at*q95c<|hS+?1b5cCqB6O&@mqe@pwP z;itiG*60nr@q^vI#e<(?PWZ0l1NzW>t)=W`xQqY9bZ$fM zyAyrp9AADDxegmYz^C@XV&>wd_t(Ca_26Z2M*p$r&|l^IzQszE-FsVCY$Ce9=z%%VzU4o4GEZ}ur^!5f6aBsn zUEYFU#o?Kj7dY1jyv4UtUx5Apy?N#zK7{utgY%oR19>l)h9CH1ZbzFQfixAJ!}S)_SaUJcpb>tt&C!iBLl>3M|B$ z>@$NZ^{zksj`NOk(h9KC%BU;YS3`A^&6Gmx$vh=hN2N ze*Gs89i+j#a_E&?Y;0KIR!m<0-8JV!AEn^HvDK8Dz5SEgx*30++A0N4wDmqb^}))1 z;gzi6civi_Xuv0!@A!lI5$l#8P^7;$q zJBvUccK*25CDx)1&=&Gq=iezt@C15^);#Zf>E5yHtm~b;#@W)Dvu_Fu?}|6NvS0Zs zerGRKtk#zO?TjnJc%yaw!qQLBgEmKjISL(VO^Jhhw_a}C?+^crXo%m%2WnsIYlE}4 z5YO13flQrFo`C35a&nK>cVts@_Bmn6D{qV~zU)ivNw_I2SX%B*jkQIHc^~5XBiDa% z{ekOUuHSS0j!QCUWi~q1d|<5sM%swq1zhN9r?vG7I@N@3+F@T+jI#=wJ+_mnNz0u5 z;Iw$ur{9nD69-nUkNNbIqE9*Ts2eU$wVz}!$+nO^p?H++3E4@qEo3Xn_7E<8@Dj3k zzhWEOxBLwDtZL7}cS{!q&kLzOXgt4fSh)u=+LZpmzOE9<>PHV_zczrcTW#4qn|jxh z<gg_QLZ)BKZ^&fXUgGQ3%yk4Bkk2l9Uf0XArL?9>H;UjpwBz!= zy4^gdbJ$dCbkqgN24HP@l5@DI|J<|oLhs}0qZxP}CMTf**%YQGq;l!YIRijC_B!s* zBvwA_{%tGm_Ncu~-F?N7hOrK*X76Lbm4SaUJ`ozAts2^iQ=c!!S|r-$ye9le&`B*a zbuG`-QXd$9(y@GK8hVv|Bw0F2XF`nNIo_FmPf=Gh@&SK%T`S`_M4L;HUDGY=r3}$K zeafbqUVx=uGCTdmS2Tw8#84i#vYQ^JE@HbCXfm+&&8#~Ne3jT+hRxnlMg`Of|0=e7*{&lbH-eJnSA7o_4yrk{rwYA=%*5%t=zv22d*FmlWT>H89alOSg z$DtXWd)kUTRlJ~iLs@psWN{{qb;m$bit;?RkX4{dl_ z+icik??A%`;ayzu&_p)CC-73N64?N+b>0QP1txd)EwZNE z5t!Vh*xy!PpU(Jr`|N=C+k%NFi7h{Q^2sIpe0`dZ!t2Kl1rwK@)35G>24bt+`|w&T zv7I$neO^nS-agd6IU9g>)z$|M#E47D@uiIqq08@OPOfXn=--($&YJSyHNnJxkf+y> zaRDlwHDx)!z2h6$)Ji_wU;J53@0_Y%(%g3t`!p`-jDOLZ@@Hgy>oV}XmAX++S_$li zkm@u&o;kT`4%cIR*Vzcw{BK{({P=U5noiX%d9O2=SllP#E7m%(}#tF;n;Z11iw0zXEc-NYO` zM;;ouI;y;p=a>&qrO0}XBp zDyGCZH74a7m0!;Dz#|)>y@2(s7+s$+vu7(DVa%L69)4AOu)$B1ceJjdXWOz|?SSvL z!1YZnjH`{>);hPSgZ7F!pD3IA%^I(<>FoMyg`T!r8M9<%?w?I=#z5h~=n_3R$m+qt zOmN`tp>lEX=qT10o;}oRkNu93j74yQBf+_61nU<#b#0j#>sAcdqWP>_z^-*`5%6eT zq89PyHr~}<7~y3P_H27S-&v=$FJ>rp{X5#w5%`}&oTE+WFwwToVe*czJ%{!Vf)n07 z#2Qgmta|F%O|9<)6Fc;St`tOrfCq2EHxoTmqe#0D!{$>(~68*NZ#;J|z zf9#%;L;XkA8pGe^qFLT^Wr*SL8u6U9-<2Iz1=x|m`xZR7k*gK{`T+iw3=!WCMRxRq zr{mZ)qPGvoMd)^9uxgAgyXQMy^PsnGUsh*3anl;)Q7!-9D~IOl;rVNCf+`jDL^*jwORcg<7G+~wOg=AxT*ulPLp_HMN;zLlMo-FTTp zzskEZ^9pR~c=xN&RvTkz2VT+WT69v4?QFdJE8bBZeAmA3>+&wR%Lz)~W_ISp z6drD;-QKi)h1yz4ThMmw8gRLlaX-oUmjx5U!T*1t-v}3RaFWDFRu4X&1s^UCw}KDp z0ae88Y&>K&9%-@p_!97HVCEU|@uSj1X)}e7H5cMzU>6@hCB7R29O7ff+YTJU(WB^{ z0bh1gJ8fm^KYD}VUE1qCJ_5+*EVT-90K}KFUu}BK>al%?d&VH1-3okb(KY2yiNWKN zwb6roW=@UHxs$pre(Bw%Z+rECzY|^dwKh6F{(VK(XG8b4oT$Hw`&Xv^l!}Pa~@YNR}Po>_jNxytZErM`cOI@_V8t*`->arvqvVa zoqQSVf0}&sJTS5sXe@c&N zgFo7t=MLytK5o^=iXa!!qc`4ODLp!`a}0K46|l>Hgzlg?8uU0QG=^Fw#L7# zRT;=Ja$WnX4$r6NW2J@PQ8g-Z@NFr!q8#@8M)FdZn|w*esr*y<|046pmDB-$jJ7HR zd7W$VN5<)QMj)?=x+P8HPcB;00xYZg2kORiZw6yti0(I6wRd^9jQyA~`jl<&M_)94 zk8Sups=(Qkeek)n=K@_z_1dHZO@_DQ;Pxx@NzD{Tj>|60wsUidH)jB+kzv3@oOob6 z@rBf9*#`*Tzpq?;=qs9^XV(j&&Wd99y4TG;#f}VLIrn>?gZQ4YN0VnEDvx|3I&``= z&!j_Zj@6Mb1t)xNwYa>|<~o4L9@qEZRS$r!T33t?>MlE?rhnf z8q>NBUzN_;)0rB|{n;bF)BFs`3tPUNtF5jFpNfc9poQb}ll}zJAifoTuX)P+cDLuZ z#W#c{YrXThg&c@ixPHddyZK z|G*OtTJMp1lkLIFpp6Yjg zGM@*T(0mtsfo|4aiCiv~T+RuvC2LRZ=%mAG!>tNJiH4zJr6kzjl@{2YCM^nLg^DY?)3T-O{$; z&`?`;Zn&*HBh&`Im5ks z=NzIqFjum#M{xw@ZpHfLgd?{)=L;~d<2QTxbz(RMKiYI^xBI54sri4?_RF+=M%fJ9 zJ+c|Nqm0=L*ZQ-(d$#wK{~Knn>vZ=aoAzA(LD!nniZ0huYd>SwZ+KSk z7=FD8Um$C}A6l;pUJ;7l0uScrQ_nLa6r;VEVjQ&jm1fpb=-agRr*Fi2_aE$lUsMnB2I_rckKkX)GkTN@hhR|O>aPa>%ANR2cUxI?9X#8J zAH4zjl8f$_e{NRYjqD@-(gUxr?0fI1t~-2^VOi#^vP1k|FZ-7Dwhg^8ir=D~YloH5 zw8K45Gn@OIfvI?Md=UPk#mF@LMTa@_5`Tkzz7l*?5a5hT*5At*bNr@Y;x_oQQa)hj zNOC(4eA>T+Pj>%w;xot-_SV%btFj(=$|C<;_H9gOb`sBsfIE04e6`y??a4+YNI4}| z%shY>#ag0_-NY=wMKkMkY=C`MXEX5-(YkC#d{)avH^-}zyzj;Je)25#alOSQ|G^vl z{}opg*Xvw+x%P00kCs@j58s}rbi8vNblsbGuJ`bcc<8V7-#_cXq4n9{i+{8}YrcD* zFQpvEHgr0CbeoB_Y*x%~Av6+YJ#uXh<+19_4L$pia~__%o}Sd+W8{CFN zbla`qdyL{foGXgn78?U^UCFg9H79MD=8*T)hUV}g=pa6Vwyw0#rIU|cdkLfi_u;ef zoMGpBpi?BzTYDEcOrOvOYZrEdd!GEnF0Bc@W$VMN3Fa(iV3dqpFB<~>v*+G^=WF2e zt6ZuVtA11F)5*ix%btmp`6LhLnKR7iVEL7jij*fj=hd|z+o=+E6J>i?VTBd&D#ZXW?IpzS--;Jh80@#ZHV^Jm)izyC(| zeRfKH(r71m{R?#Or}p=|tQ)NTTfxn0a*t($PUF9H7}rMbgfUmET!KT>Fi{q^9@%eVd** z`NWbZIIn9R-!+Hwm2P$Pnir6h`|iD~i+YZmw{lj*qxdB7yOeYugm&Ixd~Y$XotY&q zJMqu%Cck7W_pl4q&U^SA8=U?hrvFxAu`_9>6#uS#lfyD6HIdiY^awucR{YaBeAj!u zV{~um>>S>cPvm;?IJ?>N|K5xEE?x$=FR1@ON&8yp_&H!|uo4pzHBZ$b^LG*x?=BCN zv@Byj>!Ir>C2NSUbAEHA4LZCASg+*0%lQ36X#WGAVa~dD`b(+;)DYlOJSV=G-~4`a zuHTUUoa>&*E#V)*Cc870P#JcsA1`0c|W z`EBTlf%48`__ez#qeQYud>DftTRMYA7B&O|o$c7A-Er!|1j?IQfwv7A7Uy$(BIBJ5 zeE8aHIp=jP=e)+@E!nP-s*F$+|5$8iX6WKyqgSy1ExMCh{^gmW&zkT2-WCMbj8L54 z9^hX51NLp8Z$#+Z8O>}P0)It!ui>AjtP`yTDaY40w#Ep+b~&#yeFzGz$DdA6SK zYPXrOP7920iVwIktaz8v?HTWjQE&r}j=xLZgkQPVj_=ZM({O~b=E0{Cp3UKZHdhu` zCRc#V&y~To&DoHT2`PXOH>q^FH>H3-=MSd>MLN$yMMhky$1VJGJ_aEsQu${2Rra%DVA{KNmm|Z8hN0)P;TrS`Le%GwdssJ z;GARl@pXLko&LP0F~m`{FJ&FyZF$hAM;`nbd2r{wcNl)$s#*YsAK|}c&|*FC!eine z>B6dS-wCfY+N^{pm)KIWJm