├── VERSION ├── NOTICE ├── gradle.properties ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── sparrowhawk-traits ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ ├── smithy │ │ │ ├── manifest │ │ │ ├── smithy.protocols.rpcv2.sparrowhawk.smithy │ │ │ └── smithy.protocols.idx.smithy │ │ │ └── services │ │ │ ├── software.amazon.smithy.model.validation.Validator │ │ │ └── software.amazon.smithy.model.traits.TraitService │ │ └── java │ │ └── software │ │ └── amazon │ │ └── smithy │ │ └── protocol │ │ └── traits │ │ ├── IndexedServiceTrait.java │ │ ├── IdxTrait.java │ │ ├── Rpcv2SparrowhawkTraitValidator.java │ │ ├── IndexedServiceTraitValidator.java │ │ └── Rpcv2SparrowhawkTrait.java └── build.gradle.kts ├── config ├── spotless │ └── license-header.txt └── spotbugs │ └── filter.xml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── sparrowhawk-types ├── build.gradle.kts └── src │ ├── main │ └── java │ │ └── software │ │ └── amazon │ │ └── smithy │ │ └── java │ │ └── sparrowhawk │ │ ├── SparrowhawkObject.java │ │ ├── SparrowhawkStructure.java │ │ ├── FloatMap.java │ │ ├── DoubleMap.java │ │ ├── BooleanMap.java │ │ ├── LongMap.java │ │ ├── ByteMap.java │ │ ├── ShortMap.java │ │ ├── IntegerMap.java │ │ ├── Bufferer.java │ │ ├── OptionalBlob.java │ │ ├── StringList.java │ │ ├── SparseStringList.java │ │ ├── OptionalObject.java │ │ ├── KConstants.java │ │ ├── NumberMap.java │ │ ├── StructureMap.java │ │ ├── StringMap.java │ │ ├── SparseStructureMap.java │ │ ├── SparrowhawkDeserializer.java │ │ └── SparrowhawkSerializer.java │ └── test │ └── java │ └── software │ └── amazon │ └── smithy │ └── java │ └── sparrowhawk │ ├── SparseStringListTest.java │ ├── RoundtripTests.java │ └── SparrowhawkCodegenOptionalStruct.java ├── settings.gradle.kts ├── sparrowhawk-codegen ├── src │ └── main │ │ ├── java │ │ └── software │ │ │ └── amazon │ │ │ └── smithy │ │ │ └── sparrowhawk │ │ │ └── codegen │ │ │ ├── SparrowhawkIntegration.java │ │ │ ├── CodeSections.java │ │ │ ├── Util.java │ │ │ ├── JavaDelegator.java │ │ │ ├── FieldType.java │ │ │ ├── SparrowhawkCodegenPlugin.java │ │ │ ├── SparrowhawkSettings.java │ │ │ ├── SparrowhawkFieldTrait.java │ │ │ ├── GenerationContext.java │ │ │ ├── JavaWriter.java │ │ │ ├── JavaImportContainer.java │ │ │ ├── SparrowhawkIndex.java │ │ │ ├── DirectedSparrowhawkCodegen.java │ │ │ ├── CommonSymbols.java │ │ │ ├── Enhancer.java │ │ │ └── SparrowhawkSymbolVisitor.java │ │ └── resources │ │ └── software │ │ └── amazon │ │ └── smithy │ │ └── sparrowhawk │ │ └── codegen │ │ └── reserved-words.txt └── build.gradle.kts ├── gradlew.bat ├── CONTRIBUTING.md ├── gradlew └── LICENSE /VERSION: -------------------------------------------------------------------------------- 1 | 0.0.1 2 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.parallel=true 2 | org.gradle.jvmargs='-Dfile.encoding=UTF-8' 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smithy-lang/smithy-sparrowhawk-java/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /sparrowhawk-traits/src/main/resources/META-INF/smithy/manifest: -------------------------------------------------------------------------------- 1 | smithy.protocols.idx.smithy 2 | smithy.protocols.rpcv2.sparrowhawk.smithy -------------------------------------------------------------------------------- /config/spotless/license-header.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Gradle project-specific cache directory 2 | .gradle 3 | 4 | # Ignore Gradle build output directory 5 | build/ 6 | 7 | # Ignore intellij files 8 | .idea 9 | 10 | #Ignore mac files 11 | .DS_Store 12 | -------------------------------------------------------------------------------- /sparrowhawk-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator: -------------------------------------------------------------------------------- 1 | software.amazon.smithy.protocol.traits.IndexedServiceTraitValidator 2 | software.amazon.smithy.protocol.traits.Rpcv2SparrowhawkTraitValidator -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /sparrowhawk-traits/src/main/resources/META-INF/services/software.amazon.smithy.model.traits.TraitService: -------------------------------------------------------------------------------- 1 | software.amazon.smithy.protocol.traits.IdxTrait$Provider 2 | software.amazon.smithy.protocol.traits.IndexedServiceTrait$Provider 3 | software.amazon.smithy.protocol.traits.Rpcv2SparrowhawkTrait$Provider -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /sparrowhawk-types/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("smithy-sparrowhawk-java.module-conventions") 3 | } 4 | 5 | description = "This module provides sparrowhawk functionality" 6 | 7 | extra["displayName"] = "Smithy :: Java :: Sparrowhawk" 8 | extra["moduleName"] = "software.amazon.smithy.java.sparrowhawk" 9 | 10 | dependencies { 11 | } 12 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | plugins { 3 | id("org.jetbrains.kotlin.jvm") version "2.0.0" 4 | } 5 | } 6 | plugins { 7 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0" 8 | } 9 | rootProject.name = "smithy-sparrowhawk-java" 10 | 11 | include("sparrowhawk-codegen") 12 | include("sparrowhawk-types") 13 | include("sparrowhawk-traits") 14 | -------------------------------------------------------------------------------- /sparrowhawk-traits/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("smithy-sparrowhawk-java.module-conventions") 3 | } 4 | 5 | description = "This module provides traits that are currently unique to the Sparrowhawk format" 6 | 7 | extra["displayName"] = "Smithy :: Java :: Sparrowhawk Traits" 8 | extra["moduleName"] = "software.amazon.smithy.java.sparrowhawk.traits" 9 | 10 | dependencies { 11 | api(libs.smithy.model) 12 | } 13 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/SparrowhawkObject.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | public interface SparrowhawkObject { 9 | void decodeFrom(SparrowhawkDeserializer d); 10 | 11 | void encodeTo(SparrowhawkSerializer s); 12 | 13 | int size(); 14 | } 15 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/SparrowhawkIntegration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import software.amazon.smithy.codegen.core.SmithyIntegration; 9 | 10 | public interface SparrowhawkIntegration extends SmithyIntegration { 11 | } 12 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/CodeSections.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import software.amazon.smithy.utils.CodeSection; 9 | 10 | public final class CodeSections { 11 | public record StartClassSection(StructureGenerator generator) implements CodeSection {} 12 | 13 | public record EndClassSection(StructureGenerator generator) implements CodeSection {} 14 | 15 | private CodeSections() {} 16 | } 17 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("smithy-sparrowhawk-java.module-conventions") 3 | id("smithy-sparrowhawk-java.integ-test-conventions") 4 | } 5 | 6 | description = "This module provides the codegen functionality for Smithy Sparrowhawk" 7 | group = "software.amazon.smithy.java.codegen" 8 | 9 | extra["displayName"] = "Smithy :: Java :: Codegen :: Sparrowhawk" 10 | extra["moduleName"] = "software.amazon.smithy.java.sparrowhawk.codegen" 11 | 12 | dependencies { 13 | implementation(libs.smithy.codegen) 14 | implementation(project(":sparrowhawk-traits")) 15 | implementation(project(":sparrowhawk-types")) 16 | } 17 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/Util.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import java.util.stream.Stream; 9 | import software.amazon.smithy.model.shapes.Shape; 10 | 11 | public final class Util { 12 | public static boolean isStructure(Shape shape) { 13 | return shape.isStructureShape() || shape.isUnionShape(); 14 | } 15 | 16 | public static Iterable iter(Stream stream) { 17 | return stream::iterator; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/SparrowhawkStructure.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import java.util.concurrent.Flow; 9 | 10 | public interface SparrowhawkStructure extends SparrowhawkObject { 11 | Class getConvertedType(); 12 | 13 | default T convertTo() { 14 | throw new UnsupportedOperationException(); 15 | } 16 | 17 | default T convertTo(Flow.Publisher publisher) { 18 | throw new UnsupportedOperationException(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/resources/software/amazon/smithy/sparrowhawk/codegen/reserved-words.txt: -------------------------------------------------------------------------------- 1 | abstract 2 | continue 3 | for 4 | new 5 | switch 6 | assert 7 | default 8 | if 9 | package 10 | synchronized 11 | boolean 12 | do 13 | goto 14 | private 15 | this 16 | break 17 | double 18 | implements 19 | protected 20 | throw 21 | byte 22 | else 23 | import 24 | public 25 | throws 26 | case 27 | enum 28 | instanceof 29 | return 30 | transient 31 | catch 32 | extends 33 | int 34 | short 35 | try 36 | char 37 | final 38 | interface 39 | static 40 | void 41 | class 42 | finally 43 | long 44 | strictfp 45 | volatile 46 | const 47 | float 48 | native 49 | super 50 | while 51 | _ 52 | true 53 | false 54 | null 55 | var 56 | -------------------------------------------------------------------------------- /config/spotbugs/filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/JavaDelegator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import software.amazon.smithy.build.FileManifest; 9 | import software.amazon.smithy.codegen.core.SymbolProvider; 10 | import software.amazon.smithy.codegen.core.WriterDelegator; 11 | 12 | public class JavaDelegator extends WriterDelegator { 13 | public JavaDelegator(FileManifest fileManifest, SymbolProvider symbolProvider, SparrowhawkSettings settings) { 14 | super(fileManifest, symbolProvider, new JavaWriter.JavaWriterFactory(settings)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/FieldType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import software.amazon.smithy.java.sparrowhawk.KConstants; 9 | 10 | public enum FieldType { 11 | VARINT("varint", "VARINT", KConstants.T_VARINT), 12 | LIST("list", "LIST", KConstants.T_LIST), 13 | FOUR_BYTE("fourByte", "FOUR_BYTE", KConstants.T_FOUR), 14 | EIGHT_BYTE("eightByte", "EIGHT_BYTE", KConstants.T_EIGHT), 15 | ; 16 | 17 | public final String lowercaseId; 18 | public final String uppercaseId; 19 | public final int wireType; 20 | 21 | FieldType(String lowercaseId, String uppercaseId, int wireType) { 22 | this.lowercaseId = lowercaseId; 23 | this.uppercaseId = uppercaseId; 24 | this.wireType = wireType; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sparrowhawk-traits/src/main/resources/META-INF/smithy/smithy.protocols.rpcv2.sparrowhawk.smithy: -------------------------------------------------------------------------------- 1 | $version: "2.0" 2 | 3 | namespace smithy.protocols 4 | 5 | use smithy.api#cors 6 | use smithy.api#endpoint 7 | use smithy.api#hostLabel 8 | use smithy.api#httpError 9 | 10 | /// An RPC-based protocol that serializes Sparrowhawk payloads. 11 | @trait(selector: "service") 12 | @protocolDefinition(traits: [ 13 | cors 14 | endpoint 15 | hostLabel 16 | httpError 17 | ]) 18 | @traitValidators( 19 | "rpcv2Sparrowhawk.NoDocuments": { 20 | selector: "service ~> member :test(> document)" 21 | message: "This protocol does not support document types" 22 | } 23 | ) 24 | structure rpcv2Sparrowhawk { 25 | /// Priority ordered list of supported HTTP protocol versions. 26 | http: StringList 27 | 28 | /// Priority ordered list of supported HTTP protocol versions 29 | /// that are required when using event streams. 30 | eventStreamHttp: StringList 31 | } 32 | 33 | /// A list of String shapes. 34 | @private 35 | list StringList { 36 | member: String 37 | } -------------------------------------------------------------------------------- /sparrowhawk-traits/src/main/resources/META-INF/smithy/smithy.protocols.idx.smithy: -------------------------------------------------------------------------------- 1 | $version: "2.0" 2 | 3 | namespace smithy.protocols 4 | 5 | /// Indicates that a particular service is indexed. 6 | /// An indexed service must apply the @idx trait to every structure or union member in its closure, 7 | /// except those members that are used for streaming payloads. 8 | @trait(selector: "service") 9 | structure indexed {} 10 | 11 | /// Member index for an indexed protocol, used by an indexed service. 12 | /// idx values must start at 1 and increase monotonically by 1 with no gaps. 13 | @trait(selector: ":is(structure, union) :not([trait|mixin]) > member") 14 | @range(min: 1) 15 | integer idx 16 | 17 | /// Marks a structure's place in an index hierarchy. 18 | /// Some indexed protocols allow for composition of indexes from related shapes into one index space 19 | /// disambiguating conflicts between idx values. This is largely useful for non-Smithy protocols that 20 | /// are lossily translated to Smithy. 21 | @trait(selector: "structure :not([trait|mixin])") 22 | @length(min: 1) 23 | list hierarchicalIdx { 24 | member: ChildId 25 | } 26 | 27 | /// The shapeId of a child structure in a hierarchicalIdx relationship. 28 | @idRef(selector: "structure :not([trait|mixin])") 29 | @private 30 | string ChildId 31 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/FloatMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.encodeFourBListLength; 9 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.ulongSize; 10 | 11 | public final class FloatMap extends NumberMap { 12 | @Override 13 | protected Float[] newArray(int len) { 14 | return new Float[len]; 15 | } 16 | 17 | @Override 18 | protected int decodeValueCount(int encodedCount) { 19 | return KConstants.decodeFourByteListLengthChecked(encodedCount); 20 | } 21 | 22 | @Override 23 | protected Float decode(SparrowhawkDeserializer d) { 24 | return d.f4(); 25 | } 26 | 27 | @Override 28 | protected void writeValues(SparrowhawkSerializer s, Float[] values) { 29 | s.writeFloatList(values); 30 | } 31 | 32 | @Override 33 | protected int sizeofValues(Float[] elements) { 34 | int n = elements.length; 35 | int size = ulongSize(encodeFourBListLength(n)); 36 | return size + (4 * n); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/DoubleMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.encodeFourBListLength; 9 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.ulongSize; 10 | 11 | public final class DoubleMap extends NumberMap { 12 | @Override 13 | protected Double[] newArray(int len) { 14 | return new Double[len]; 15 | } 16 | 17 | @Override 18 | protected int decodeValueCount(int encodedCount) { 19 | return KConstants.decodeEightByteListLengthChecked(encodedCount); 20 | } 21 | 22 | @Override 23 | protected Double decode(SparrowhawkDeserializer d) { 24 | return d.d8(); 25 | } 26 | 27 | @Override 28 | protected void writeValues(SparrowhawkSerializer s, Double[] values) { 29 | s.writeDoubleList(values); 30 | } 31 | 32 | @Override 33 | protected int sizeofValues(Double[] elements) { 34 | int n = elements.length; 35 | int size = ulongSize(encodeFourBListLength(n)); 36 | return size + (8 * n); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/BooleanMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.encodeVarintListLength; 9 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.ulongSize; 10 | 11 | public final class BooleanMap extends NumberMap { 12 | @Override 13 | protected Boolean[] newArray(int len) { 14 | return new Boolean[len]; 15 | } 16 | 17 | @Override 18 | protected int decodeValueCount(int encodedCount) { 19 | return KConstants.decodeVarintListLengthChecked(encodedCount); 20 | } 21 | 22 | @Override 23 | protected Boolean decode(SparrowhawkDeserializer d) { 24 | return d.bool(); 25 | } 26 | 27 | @Override 28 | protected void writeValues(SparrowhawkSerializer s, Boolean[] values) { 29 | s.writeBooleanList(values); 30 | } 31 | 32 | @Override 33 | protected int sizeofValues(Boolean[] elements) { 34 | int n = elements.length; 35 | int size = ulongSize(encodeVarintListLength(n)); 36 | return size + elements.length; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/LongMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.encodeVarintListLength; 9 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.ulongSize; 10 | 11 | public final class LongMap extends NumberMap { 12 | @Override 13 | protected Long[] newArray(int len) { 14 | return new Long[len]; 15 | } 16 | 17 | @Override 18 | protected int decodeValueCount(int encodedCount) { 19 | return KConstants.decodeVarintListLengthChecked(encodedCount); 20 | } 21 | 22 | @Override 23 | protected Long decode(SparrowhawkDeserializer d) { 24 | return d.varL(); 25 | } 26 | 27 | @Override 28 | protected void writeValues(SparrowhawkSerializer s, Long[] values) { 29 | s.writeLongList(values); 30 | } 31 | 32 | @Override 33 | protected int sizeofValues(Long[] elements) { 34 | int n = elements.length; 35 | int size = ulongSize(encodeVarintListLength(n)); 36 | for (int i = 0; i < n; i++) { 37 | size += SparrowhawkSerializer.longSize(elements[i]); 38 | } 39 | return size; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/ByteMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.encodeVarintListLength; 9 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.ulongSize; 10 | 11 | public final class ByteMap extends NumberMap { 12 | @Override 13 | protected Byte[] newArray(int len) { 14 | return new Byte[len]; 15 | } 16 | 17 | @Override 18 | protected int decodeValueCount(int encodedCount) { 19 | return KConstants.decodeVarintListLengthChecked(encodedCount); 20 | } 21 | 22 | @Override 23 | protected Byte decode(SparrowhawkDeserializer d) { 24 | return (byte) d.varI(); 25 | } 26 | 27 | @Override 28 | protected void writeValues(SparrowhawkSerializer s, Byte[] values) { 29 | s.writeByteList(values); 30 | } 31 | 32 | @Override 33 | protected int sizeofValues(Byte[] elements) { 34 | int n = elements.length; 35 | int size = ulongSize(encodeVarintListLength(n)); 36 | for (int i = 0; i < n; i++) { 37 | size += SparrowhawkSerializer.intSize(elements[i]); 38 | } 39 | return size; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/ShortMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.encodeVarintListLength; 9 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.ulongSize; 10 | 11 | public final class ShortMap extends NumberMap { 12 | @Override 13 | protected Short[] newArray(int len) { 14 | return new Short[len]; 15 | } 16 | 17 | @Override 18 | protected int decodeValueCount(int encodedCount) { 19 | return KConstants.decodeVarintListLengthChecked(encodedCount); 20 | } 21 | 22 | @Override 23 | protected Short decode(SparrowhawkDeserializer d) { 24 | return (short) d.varI(); 25 | } 26 | 27 | @Override 28 | protected void writeValues(SparrowhawkSerializer s, Short[] values) { 29 | s.writeShortList(values); 30 | } 31 | 32 | @Override 33 | protected int sizeofValues(Short[] elements) { 34 | int n = elements.length; 35 | int size = ulongSize(encodeVarintListLength(n)); 36 | for (int i = 0; i < n; i++) { 37 | size += SparrowhawkSerializer.intSize(elements[i]); 38 | } 39 | return size; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/IntegerMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.encodeVarintListLength; 9 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.ulongSize; 10 | 11 | public final class IntegerMap extends NumberMap { 12 | @Override 13 | protected int decodeValueCount(int encodedCount) { 14 | return KConstants.decodeVarintListLengthChecked(encodedCount); 15 | } 16 | 17 | @Override 18 | protected Integer decode(SparrowhawkDeserializer d) { 19 | return d.varI(); 20 | } 21 | 22 | @Override 23 | protected int sizeofValues(Integer[] elements) { 24 | int n = elements.length; 25 | int size = ulongSize(encodeVarintListLength(n)); 26 | for (int i = 0; i < n; i++) { 27 | size += SparrowhawkSerializer.intSize(elements[i]); 28 | } 29 | return size; 30 | } 31 | 32 | @Override 33 | protected Integer[] newArray(int len) { 34 | return new Integer[len]; 35 | } 36 | 37 | @Override 38 | protected void writeValues(SparrowhawkSerializer s, Integer[] values) { 39 | s.writeIntegerList(values); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/SparrowhawkCodegenPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import software.amazon.smithy.build.PluginContext; 9 | import software.amazon.smithy.build.SmithyBuildPlugin; 10 | import software.amazon.smithy.codegen.core.directed.CodegenDirector; 11 | 12 | public class SparrowhawkCodegenPlugin implements SmithyBuildPlugin { 13 | @Override 14 | public String getName() { 15 | return "sparrowhawk-codegen"; 16 | } 17 | 18 | @Override 19 | public void execute(PluginContext context) { 20 | var director = new CodegenDirector(); 21 | 22 | var settings = SparrowhawkSettings.from(context.getSettings()); 23 | director.settings(settings); 24 | director.directedCodegen(new DirectedSparrowhawkCodegen()); 25 | director.fileManifest(context.getFileManifest()); 26 | director.service(settings.getService()); 27 | director.model(context.getModel()); 28 | director.integrationClass(SparrowhawkIntegration.class); 29 | director.performDefaultCodegenTransforms(); 30 | director.createDedicatedInputsAndOutputs(); 31 | director.run(); 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /sparrowhawk-traits/src/main/java/software/amazon/smithy/protocol/traits/IndexedServiceTrait.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.protocol.traits; 7 | 8 | import java.util.Collections; 9 | import software.amazon.smithy.model.SourceLocation; 10 | import software.amazon.smithy.model.node.Node; 11 | import software.amazon.smithy.model.node.ObjectNode; 12 | import software.amazon.smithy.model.shapes.ShapeId; 13 | import software.amazon.smithy.model.traits.AbstractTrait; 14 | import software.amazon.smithy.model.traits.Trait; 15 | import software.amazon.smithy.model.traits.TraitService; 16 | 17 | public final class IndexedServiceTrait extends AbstractTrait { 18 | public static final ShapeId ID = ShapeId.from("smithy.protocols#indexed"); 19 | 20 | public IndexedServiceTrait(SourceLocation sourceLocation) { 21 | super(ID, sourceLocation); 22 | } 23 | 24 | public IndexedServiceTrait() { 25 | this(SourceLocation.NONE); 26 | } 27 | 28 | @Override 29 | protected Node createNode() { 30 | return new ObjectNode(Collections.emptyMap(), getSourceLocation()); 31 | } 32 | 33 | public static final class Provider implements TraitService { 34 | 35 | @Override 36 | public Trait createTrait(ShapeId target, Node value) { 37 | IndexedServiceTrait result = new IndexedServiceTrait(value.getSourceLocation()); 38 | result.setNodeCache(value); 39 | return result; 40 | } 41 | 42 | @Override 43 | public ShapeId getShapeId() { 44 | return ID; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/SparrowhawkSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import java.util.Objects; 9 | import software.amazon.smithy.model.node.ObjectNode; 10 | import software.amazon.smithy.model.shapes.ShapeId; 11 | 12 | public final class SparrowhawkSettings { 13 | private static final String SERVICE = "service"; 14 | private static final String USE_INSTANT_FOR_TIMESTAMP = "useInstantForTimestamp"; 15 | private static final String HEADER_STRING = "headerString"; 16 | 17 | private final ShapeId service; 18 | private final boolean useInstant; 19 | private final String header; 20 | 21 | private SparrowhawkSettings(ShapeId service, boolean useInstant, String header) { 22 | this.service = service; 23 | this.useInstant = useInstant; 24 | this.header = header; 25 | } 26 | 27 | public static SparrowhawkSettings from(ObjectNode config) { 28 | // config.warnIfAdditionalProperties(List.of(SERVICE)); 29 | return new SparrowhawkSettings( 30 | config.expectStringMember(SERVICE).expectShapeId(), 31 | config.expectBooleanMember(USE_INSTANT_FOR_TIMESTAMP).getValue(), 32 | config.getStringMemberOrDefault(HEADER_STRING, null) 33 | ); 34 | } 35 | 36 | public ShapeId getService() { 37 | return Objects.requireNonNull(service, SERVICE + " not set"); 38 | } 39 | 40 | public boolean useInstant() { 41 | return useInstant; 42 | } 43 | 44 | public String getHeader() { 45 | return header; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/SparrowhawkFieldTrait.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import software.amazon.smithy.model.node.Node; 9 | import software.amazon.smithy.model.shapes.ShapeId; 10 | import software.amazon.smithy.model.traits.AbstractTrait; 11 | 12 | public final class SparrowhawkFieldTrait extends AbstractTrait { 13 | static final ShapeId ID = ShapeId.fromParts("sparrowhawk", "field"); 14 | 15 | private final FieldType type; 16 | private final int fieldSetIdx; 17 | private final int typeIdx; 18 | private final boolean required; 19 | 20 | public SparrowhawkFieldTrait(FieldType type, int fieldSetIdx, int typeIdx, boolean required) { 21 | super(ID, Node.objectNode()); 22 | if (fieldSetIdx < 0) { throw new IllegalArgumentException("illegal fieldSetIdx " + fieldSetIdx); } 23 | if (typeIdx < 1 || typeIdx > 61) { throw new IllegalArgumentException("Illegal typeIdx" + typeIdx); } 24 | this.type = type; 25 | this.fieldSetIdx = fieldSetIdx; 26 | this.typeIdx = typeIdx; 27 | this.required = required; 28 | } 29 | 30 | public FieldType getType() { 31 | return type; 32 | } 33 | 34 | public int getFieldSetIdx() { 35 | return fieldSetIdx; 36 | } 37 | 38 | public int getTypeIdx() { 39 | return typeIdx; 40 | } 41 | 42 | public boolean isRequired() { 43 | return required; 44 | } 45 | 46 | @Override 47 | protected Node createNode() { 48 | return Node.objectNode(); 49 | } 50 | 51 | @Override 52 | public boolean isSynthetic() { 53 | return true; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /sparrowhawk-traits/src/main/java/software/amazon/smithy/protocol/traits/IdxTrait.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.protocol.traits; 7 | 8 | import software.amazon.smithy.model.SourceLocation; 9 | import software.amazon.smithy.model.node.Node; 10 | import software.amazon.smithy.model.node.NumberNode; 11 | import software.amazon.smithy.model.shapes.ShapeId; 12 | import software.amazon.smithy.model.traits.AbstractTrait; 13 | import software.amazon.smithy.model.traits.Trait; 14 | import software.amazon.smithy.model.traits.TraitService; 15 | 16 | public class IdxTrait extends AbstractTrait { 17 | 18 | public static final ShapeId ID = ShapeId.from("smithy.protocols#idx"); 19 | 20 | private final int value; 21 | 22 | public IdxTrait(int value, SourceLocation sourceLocation) { 23 | super(ID, sourceLocation); 24 | this.value = value; 25 | } 26 | 27 | public IdxTrait(int value) { 28 | this(value, SourceLocation.NONE); 29 | } 30 | 31 | public int getValue() { 32 | return value; 33 | } 34 | 35 | @Override 36 | protected Node createNode() { 37 | return new NumberNode(value, getSourceLocation()); 38 | } 39 | 40 | public static final class Provider implements TraitService { 41 | 42 | @Override 43 | public Trait createTrait(ShapeId target, Node value) { 44 | IdxTrait result = new IdxTrait( 45 | value.expectNumberNode().getValue().intValue(), 46 | value.getSourceLocation() 47 | ); 48 | result.setNodeCache(value); 49 | return result; 50 | } 51 | 52 | @Override 53 | public ShapeId getShapeId() { 54 | return ID; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | junit5 = "5.10.2" 3 | hamcrest = "2.1" 4 | smithy = "1.50.0" 5 | jmh = "0.7.2" 6 | test-logger-plugin = "4.0.0" 7 | spotbugs = "6.0.9" 8 | spotless = "6.25.0" 9 | smithy-gradle = "1.0.0" 10 | assertj = "3.25.1" 11 | 12 | [libraries] 13 | smithy-model = { module = "software.amazon.smithy:smithy-model", version.ref = "smithy" } 14 | smithy-codegen = { module = "software.amazon.smithy:smithy-codegen-core", version.ref = "smithy" } 15 | smithy-aws-traits = { module = "software.amazon.smithy:smithy-aws-traits", version.ref = "smithy" } 16 | smithy-protocol-traits = { module = "software.amazon.smithy:smithy-protocol-traits", version.ref = "smithy" } 17 | smithy-aws-protocol-tests = { module = "software.amazon.smithy:smithy-aws-protocol-tests", version.ref = "smithy" } 18 | 19 | # Test dependencies 20 | junit-bom = { module = "org.junit:junit-bom", version.ref = "junit5" } 21 | junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit5" } 22 | junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit5" } 23 | junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit5" } 24 | hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" } 25 | assertj-core = { module = "org.assertj:assertj-core", version.ref = "assertj"} 26 | 27 | # plugin artifacts for buildsrc plugins 28 | test-logger-plugin = { module = "com.adarshr:gradle-test-logger-plugin", version.ref = "test-logger-plugin" } 29 | spotbugs = { module = "com.github.spotbugs.snom:spotbugs-gradle-plugin", version.ref = "spotbugs" } 30 | spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" } 31 | smithy-gradle-base = { module = "software.amazon.smithy.gradle:smithy-base", version.ref = "smithy-gradle" } 32 | 33 | [plugins] 34 | jmh = { id = "me.champeau.jmh", version.ref = "jmh" } 35 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/GenerationContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import software.amazon.smithy.build.FileManifest; 11 | import software.amazon.smithy.codegen.core.CodegenContext; 12 | import software.amazon.smithy.codegen.core.SymbolProvider; 13 | import software.amazon.smithy.codegen.core.WriterDelegator; 14 | import software.amazon.smithy.model.Model; 15 | import software.amazon.smithy.utils.SmithyBuilder; 16 | import software.amazon.smithy.utils.ToSmithyBuilder; 17 | 18 | public record GenerationContext( 19 | Model model, SparrowhawkSettings settings, SymbolProvider symbolProvider, 20 | WriterDelegator writerDelegator, List integrations, 21 | FileManifest fileManifest 22 | ) 23 | implements CodegenContext, 24 | ToSmithyBuilder { 25 | 26 | public static Builder builder() { return new Builder(); } 27 | 28 | @Override 29 | public SmithyBuilder toBuilder() { 30 | return new Builder().model(model) 31 | .settings(settings) 32 | .symbolProvider(symbolProvider) 33 | .writerDelegator(writerDelegator) 34 | .integrations(integrations) 35 | .fileManifest(fileManifest); 36 | } 37 | 38 | public static final class Builder implements SmithyBuilder { 39 | 40 | private Model model; 41 | private SparrowhawkSettings settings; 42 | private SymbolProvider symbolProvider; 43 | private WriterDelegator writerDelegator; 44 | private List integrations = new ArrayList<>(); 45 | private FileManifest fileManifest; 46 | 47 | public Builder model(Model m) { this.model = m; return this; } 48 | 49 | public Builder settings(SparrowhawkSettings s) { this.settings = s; return this; } 50 | 51 | public Builder symbolProvider(SymbolProvider s) { this.symbolProvider = s; return this; } 52 | 53 | public Builder writerDelegator(WriterDelegator w) { this.writerDelegator = w; return this; } 54 | 55 | public Builder integrations(List i) { this.integrations = i; return this; } 56 | 57 | public Builder fileManifest(FileManifest f) { this.fileManifest = f; return this; } 58 | 59 | @Override 60 | public GenerationContext build() { 61 | return new GenerationContext(model, settings, symbolProvider, writerDelegator, integrations, fileManifest); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /sparrowhawk-traits/src/main/java/software/amazon/smithy/protocol/traits/Rpcv2SparrowhawkTraitValidator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.protocol.traits; 7 | 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import software.amazon.smithy.model.Model; 12 | import software.amazon.smithy.model.shapes.ServiceShape; 13 | import software.amazon.smithy.model.validation.AbstractValidator; 14 | import software.amazon.smithy.model.validation.ValidationEvent; 15 | import software.amazon.smithy.utils.SmithyInternalApi; 16 | 17 | /** 18 | * Validates models implementing the {@code Rpcv2SparrowhawkTrait} against its constraints by: 19 | * 20 | * - Ensuring that every entry in {@code eventStreamHttp} also appears in the {@code http} property 21 | * of a protocol trait. 22 | */ 23 | @SmithyInternalApi 24 | public final class Rpcv2SparrowhawkTraitValidator extends AbstractValidator { 25 | 26 | @Override 27 | public List validate(Model model) { 28 | List events = new ArrayList<>(); 29 | for (ServiceShape serviceShape : model.getServiceShapesWithTrait(Rpcv2SparrowhawkTrait.class)) { 30 | Rpcv2SparrowhawkTrait protocolTrait = serviceShape.expectTrait(Rpcv2SparrowhawkTrait.class); 31 | if (serviceShape.getTrait(IndexedServiceTrait.class).isEmpty()) { 32 | events.add( 33 | error( 34 | serviceShape, 35 | protocolTrait, 36 | String.format( 37 | "Protocol trait `%s` requires that the service shape also have a %s trait. Didn't find it on `%s`", 38 | protocolTrait.toShapeId(), 39 | IndexedServiceTrait.ID, 40 | serviceShape.getId() 41 | ) 42 | ) 43 | ); 44 | } 45 | List invalid = new ArrayList<>(protocolTrait.getEventStreamHttp()); 46 | invalid.removeAll(protocolTrait.getHttp()); 47 | if (!invalid.isEmpty()) { 48 | events.add( 49 | error( 50 | serviceShape, 51 | protocolTrait, 52 | String.format( 53 | "The following values of the `eventStreamHttp` property do " 54 | + "not also appear in the `http` property of the %s protocol " 55 | + "trait: %s", 56 | protocolTrait.toShapeId(), 57 | invalid 58 | ) 59 | ) 60 | ); 61 | } 62 | } 63 | return events; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/Bufferer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import java.nio.ByteBuffer; 9 | import java.util.function.Consumer; 10 | 11 | public final class Bufferer { 12 | private static final int MAX_LENGTH_BYTES = 9; 13 | private final Consumer messageConsumer; 14 | 15 | private final byte[] lengthBytes = new byte[MAX_LENGTH_BYTES]; 16 | private int lengthPos; 17 | private int lengthRemaining; 18 | 19 | private byte[] payload; 20 | private int payloadPos; 21 | 22 | public Bufferer(Consumer messageConsumer) { 23 | this.messageConsumer = messageConsumer; 24 | } 25 | 26 | public void feed(byte[] bytes) { 27 | feed(ByteBuffer.wrap(bytes)); 28 | } 29 | 30 | public void feed(byte[] bytes, int offset, int length) { 31 | feed(ByteBuffer.wrap(bytes, offset, length)); 32 | } 33 | 34 | public void feed(ByteBuffer byteBuffer) { 35 | while (byteBuffer.remaining() > 0) { 36 | if (payload == null) { 37 | if (!determineLength(byteBuffer)) { 38 | return; 39 | } 40 | SparrowhawkDeserializer ds = new SparrowhawkDeserializer(lengthBytes); 41 | long len = KConstants.decodeByteListLength(ds.varUL()); 42 | // payload length is size of the length prefix + that many bytes 43 | payload = new byte[(int) len + ds.pos()]; 44 | payloadPos = ds.pos(); 45 | System.arraycopy(lengthBytes, 0, payload, 0, payloadPos); 46 | } 47 | 48 | int toGet = Math.min(byteBuffer.remaining(), payload.length - payloadPos); 49 | byteBuffer.get(payload, payloadPos, toGet); 50 | payloadPos += toGet; 51 | if (payload.length == payloadPos) { 52 | byte[] p = payload; 53 | payload = null; 54 | lengthPos = 0; 55 | lengthRemaining = 0; 56 | payloadPos = 0; 57 | messageConsumer.accept(p); 58 | } 59 | } 60 | } 61 | 62 | private boolean determineLength(ByteBuffer byteBuffer) { 63 | if (lengthPos == 0) { 64 | byte firstLengthByte = byteBuffer.get(); 65 | lengthBytes[lengthPos++] = firstLengthByte; 66 | if ((firstLengthByte & 1) == 1) { 67 | return true; 68 | } 69 | 70 | lengthRemaining = Integer.numberOfTrailingZeros(firstLengthByte | (1 << 8)); 71 | } 72 | 73 | int readable = byteBuffer.remaining(); 74 | if (readable == 0) { 75 | return false; 76 | } 77 | 78 | int toRead = Math.min(lengthRemaining, readable); 79 | byteBuffer.get(lengthBytes, lengthPos, toRead); 80 | lengthPos += toRead; 81 | lengthRemaining -= toRead; 82 | return lengthRemaining == 0; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/test/java/software/amazon/smithy/java/sparrowhawk/SparseStringListTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.lenPrefixedListLengthEncodedSize; 10 | 11 | import java.nio.charset.StandardCharsets; 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | import org.junit.jupiter.api.Test; 16 | 17 | 18 | public class SparseStringListTest { 19 | @Test 20 | public void sparse() { 21 | List strings = new ArrayList<>(); 22 | strings.add("one"); 23 | strings.add(null); 24 | strings.add("three"); 25 | strings.add(""); 26 | 27 | SparseStringList sparse = SparseStringList.fromList(strings); 28 | SparseStringList roundtrip = roundtrip(sparse); 29 | 30 | assertEquals(strings, roundtrip.toList()); 31 | } 32 | 33 | @Test 34 | public void oneNull() { 35 | List strings = new ArrayList<>(); 36 | strings.add(null); 37 | 38 | SparseStringList sparse = SparseStringList.fromList(strings); 39 | SparseStringList roundtrip = roundtrip(sparse); 40 | 41 | assertEquals(strings, roundtrip.toList()); 42 | } 43 | 44 | @Test 45 | public void noNulls() { 46 | List strings = new ArrayList<>(); 47 | strings.add("one"); 48 | strings.add("three"); 49 | strings.add(""); 50 | 51 | SparseStringList sparse = SparseStringList.fromList(strings); 52 | SparseStringList roundtrip = roundtrip(sparse); 53 | 54 | assertEquals(strings, roundtrip.toList()); 55 | } 56 | 57 | @Test 58 | public void bigList() { 59 | byte[] bytes = new byte[1024]; 60 | Arrays.fill(bytes, (byte) 'A'); 61 | String bigString = new String(bytes, StandardCharsets.UTF_8); 62 | List strings = new ArrayList<>(); 63 | for (int i = 0; i < 10_000; i++) { 64 | if (i % 1024 == 0) { 65 | strings.add(null); 66 | } else { 67 | strings.add(i + ":" + bigString); 68 | } 69 | } 70 | 71 | SparseStringList sparse = SparseStringList.fromList(strings); 72 | SparseStringList roundtrip = roundtrip(sparse); 73 | 74 | assertEquals(strings, roundtrip.toList()); 75 | } 76 | 77 | private static SparseStringList roundtrip(SparseStringList source) { 78 | byte[] payload = new byte[lenPrefixedListLengthEncodedSize(source.size(), source.elementCount())]; 79 | SparrowhawkSerializer s = new SparrowhawkSerializer(payload); 80 | source.encodeTo(s); 81 | 82 | SparseStringList roundtrip = new SparseStringList(); 83 | SparrowhawkDeserializer d = new SparrowhawkDeserializer(s.payload()); 84 | roundtrip.decodeFrom(d); 85 | return roundtrip; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/OptionalBlob.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.*; 9 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.byteListLengthEncodedSize; 10 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.ulongSize; 11 | 12 | import java.nio.ByteBuffer; 13 | import java.util.Objects; 14 | 15 | 16 | public final class OptionalBlob implements SparrowhawkObject { 17 | private static final long REQUIRED_LIST_0 = 0x0L; 18 | private long $list_0 = REQUIRED_LIST_0; 19 | // list fieldSet 0 index 1 20 | private static final long FIELD_ITEM = 0x8L; 21 | private ByteBuffer item; 22 | 23 | public ByteBuffer getItem() { 24 | return item; 25 | } 26 | 27 | public void setItem(ByteBuffer item) { 28 | if (item == null) { 29 | $list_0 &= ~FIELD_ITEM; 30 | } else { 31 | $list_0 |= FIELD_ITEM; 32 | } 33 | this.item = item; 34 | this.$size = -1; 35 | } 36 | 37 | public boolean hasItem() { 38 | return ($list_0 & FIELD_ITEM) != 0; 39 | } 40 | 41 | private int $size; 42 | 43 | public int size() { 44 | if ($size >= 0) { 45 | return $size; 46 | } 47 | 48 | int size = ($list_0 == 0x0L ? 0 : (ulongSize($list_0))); 49 | size += sizeListFields(); 50 | this.$size = size; 51 | return size; 52 | } 53 | 54 | private int sizeListFields() { 55 | int size = 0; 56 | if (hasItem()) { 57 | size += byteListLengthEncodedSize(item.remaining()); 58 | } 59 | return size; 60 | } 61 | 62 | public void encodeTo(SparrowhawkSerializer s) { 63 | s.writeVarUL(encodeByteListLength(size())); 64 | writeListFields(s); 65 | } 66 | 67 | private void writeListFields(SparrowhawkSerializer s) { 68 | if ($list_0 != 0x0L) { 69 | s.writeVarUL($list_0); 70 | if (hasItem()) { 71 | s.writeBytes(item); 72 | } 73 | } 74 | } 75 | 76 | public void decodeFrom(SparrowhawkDeserializer d) { 77 | int size = (int) decodeElementCount(d.varUI()); 78 | this.$size = size; 79 | int start = d.pos(); 80 | 81 | while ((d.pos() - start) < size) { 82 | long fieldSet = d.varUL(); 83 | int fieldSetIdx = ((fieldSet & 0b100) != 0) ? d.varUI() + 1 : 0; 84 | int type = (int) (fieldSet & 3); 85 | if (type == T_LIST) { 86 | if (fieldSetIdx != 0) { throw new IllegalArgumentException("unknown fieldSetIdx " + fieldSetIdx); } 87 | decodeListFieldSet0(d, fieldSet); 88 | } else { 89 | throw new RuntimeException("Unexpected field set type: " + type); 90 | } 91 | } 92 | } 93 | 94 | private void decodeListFieldSet0(SparrowhawkDeserializer d, long fieldSet) { 95 | this.$list_0 = fieldSet; 96 | if (hasItem()) { 97 | this.item = d.bytes(); 98 | } 99 | } 100 | 101 | @Override 102 | public boolean equals(Object other) { 103 | if (this == other) return true; 104 | if (!(other instanceof OptionalBlob)) return false; 105 | OptionalBlob o = (OptionalBlob) other; 106 | return Objects.equals(getItem(), o.getItem()); 107 | } 108 | 109 | @Override 110 | public int hashCode() { 111 | return Objects.hash($list_0, item, $size); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/StringList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.decodeLenPrefixedListLengthChecked; 9 | import static software.amazon.smithy.java.sparrowhawk.KConstants.encodeLenPrefixedListLength; 10 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.byteListLengthEncodedSize; 11 | 12 | import java.nio.ByteBuffer; 13 | import java.nio.charset.StandardCharsets; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | public final class StringList implements SparrowhawkObject { 18 | private static final ByteBuffer[] EMPTY = new ByteBuffer[0]; 19 | private static final StringList EMPTY_LIST; 20 | static { 21 | EMPTY_LIST = new StringList(); 22 | EMPTY_LIST.size = 0; 23 | EMPTY_LIST.values = EMPTY; 24 | } 25 | 26 | private int size; 27 | private ByteBuffer[] values; 28 | 29 | public static StringList fromList(List strings) { 30 | int len = strings.size(); 31 | if (len == 0) { 32 | return EMPTY_LIST; 33 | } 34 | 35 | StringList list = new StringList(); 36 | ByteBuffer[] values = new ByteBuffer[len]; 37 | list.values = values; 38 | list.size = encodeValues(strings, values); 39 | return list; 40 | } 41 | 42 | private static int encodeValues(List strings, ByteBuffer[] values) { 43 | if (values.length == 0) return 0; 44 | int size = 0; 45 | for (int i = 0; i < strings.size(); i++) { 46 | byte[] bytes = strings.get(i).getBytes(StandardCharsets.UTF_8); 47 | values[i] = ByteBuffer.wrap(bytes); 48 | size += byteListLengthEncodedSize(bytes.length); 49 | } 50 | return size; 51 | } 52 | 53 | public List toList() { 54 | List l = new ArrayList<>(values.length); 55 | for (ByteBuffer value : values) { 56 | l.add(string(value)); 57 | } 58 | return l; 59 | } 60 | 61 | private static String string(ByteBuffer b) { 62 | return new String(b.array(), b.arrayOffset() + b.position(), b.remaining(), StandardCharsets.UTF_8); 63 | } 64 | 65 | public int elementCount() { 66 | return values.length; 67 | } 68 | 69 | @Override 70 | public void decodeFrom(SparrowhawkDeserializer d) { 71 | int count = decodeLenPrefixedListLengthChecked(d.varUL()); 72 | if (count <= 0) { 73 | values = EMPTY; 74 | size = 0; 75 | return; 76 | } 77 | 78 | values = new ByteBuffer[count]; 79 | for (int i = 0; i < count; i++) { 80 | values[i] = d.bytes(); 81 | } 82 | } 83 | 84 | @Override 85 | public void encodeTo(SparrowhawkSerializer s) { 86 | int count = values.length; 87 | s.writeVarUL(encodeLenPrefixedListLength(count)); 88 | for (int i = 0; i < count; i++) { 89 | s.writeBytes(values[i]); 90 | } 91 | } 92 | 93 | @Override 94 | public int size() { 95 | int size = this.size; 96 | if (size >= 0) { 97 | return size; 98 | } 99 | if (values.length > 0) { 100 | for (ByteBuffer value : values) { 101 | int len = value.remaining(); 102 | size += byteListLengthEncodedSize(len) + len; 103 | } 104 | } else { 105 | size = 0; 106 | } 107 | 108 | this.size = size; 109 | return size; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/JavaWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import java.util.function.BiFunction; 9 | import software.amazon.smithy.codegen.core.CodegenException; 10 | import software.amazon.smithy.codegen.core.Symbol; 11 | import software.amazon.smithy.codegen.core.SymbolReference; 12 | import software.amazon.smithy.codegen.core.SymbolWriter; 13 | import software.amazon.smithy.model.shapes.Shape; 14 | 15 | public final class JavaWriter extends SymbolWriter { 16 | 17 | private final SparrowhawkSettings settings; 18 | private final String packageName; 19 | private boolean plainFile; 20 | 21 | public JavaWriter(SparrowhawkSettings settings, String packageName) { 22 | super(new JavaImportContainer(packageName, settings.getHeader())); 23 | this.settings = settings; 24 | this.packageName = packageName; 25 | trimBlankLines(); 26 | trimTrailingSpaces(); 27 | putFormatter('T', new JavaSymbolFormatter()); 28 | } 29 | 30 | public void setPlain(boolean plain) { 31 | this.plainFile = plain; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | if (plainFile) { 37 | return super.toString(); 38 | } 39 | return getImportContainer().toString() + "\n\n" + super.toString(); 40 | } 41 | 42 | private class JavaSymbolFormatter implements BiFunction { 43 | @Override 44 | public String apply(Object type, String indent) { 45 | if (type instanceof Symbol typeSymbol) { 46 | for (SymbolReference sr : typeSymbol.getReferences()) { 47 | addUseImports(sr); 48 | } 49 | addImport(typeSymbol, null, SymbolReference.ContextOption.USE); 50 | return typeSymbol.getProperty("shape", Shape.class).map(shape -> { 51 | if (shape.isListShape()) { 52 | addImport(typeSymbol, null, SymbolReference.ContextOption.USE); 53 | return "%s<%s>".formatted( 54 | getImportContainer().getAlias(typeSymbol), 55 | apply(typeSymbol.getProperty("value").orElseThrow(), "") 56 | ); 57 | } else if (shape.isMapShape()) { 58 | addImport(typeSymbol, null, SymbolReference.ContextOption.USE); 59 | return "%s<%s, %s>".formatted( 60 | getImportContainer().getAlias(typeSymbol), 61 | apply(typeSymbol.getProperty("key").orElseThrow(), ""), 62 | apply(typeSymbol.getProperty("value").orElseThrow(), "") 63 | ); 64 | } 65 | return getImportContainer().getAlias(typeSymbol); 66 | }).orElseGet(() -> getImportContainer().getAlias(typeSymbol)); 67 | } else if (type instanceof SymbolReference typeSymbol) { 68 | getImportContainer().importReference(typeSymbol); 69 | return getImportContainer().getAlias(typeSymbol); 70 | } else { 71 | throw new CodegenException( 72 | "Invalid type provided to $T. Expected a Symbol, but found `" + type + "`" 73 | ); 74 | } 75 | } 76 | } 77 | 78 | public static class JavaWriterFactory implements Factory { 79 | private final SparrowhawkSettings settings; 80 | 81 | public JavaWriterFactory(SparrowhawkSettings settings) { 82 | this.settings = settings; 83 | } 84 | 85 | @Override 86 | public JavaWriter apply(String filename, String namespace) { 87 | return new JavaWriter(settings, namespace); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/SparseStringList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.decodeLenPrefixedListLengthChecked; 9 | import static software.amazon.smithy.java.sparrowhawk.KConstants.encodeLenPrefixedListLength; 10 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.byteListLengthEncodedSize; 11 | 12 | import java.nio.ByteBuffer; 13 | import java.nio.charset.StandardCharsets; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | public final class SparseStringList implements SparrowhawkObject { 18 | private static final OptionalBlob EMPTY_BLOB = new OptionalBlob(); 19 | private static final OptionalBlob[] EMPTY = new OptionalBlob[0]; 20 | private static final SparseStringList EMPTY_LIST; 21 | 22 | static { 23 | EMPTY_LIST = new SparseStringList(); 24 | EMPTY_LIST.size = 0; 25 | EMPTY_LIST.values = EMPTY; 26 | } 27 | 28 | private int size; 29 | private OptionalBlob[] values; 30 | 31 | public static SparseStringList fromList(List strings) { 32 | int len = strings.size(); 33 | if (len == 0) { 34 | return EMPTY_LIST; 35 | } 36 | 37 | SparseStringList list = new SparseStringList(); 38 | OptionalBlob[] values = new OptionalBlob[len]; 39 | list.values = values; 40 | list.size = encodeValues(strings, values); 41 | return list; 42 | } 43 | 44 | private static int encodeValues(List strings, OptionalBlob[] values) { 45 | if (values.length == 0) return 0; 46 | int size = 0; 47 | for (int i = 0; i < strings.size(); i++) { 48 | String s = strings.get(i); 49 | if (s != null) { 50 | OptionalBlob blob = new OptionalBlob(); 51 | byte[] bytes = strings.get(i).getBytes(StandardCharsets.UTF_8); 52 | blob.setItem(ByteBuffer.wrap(bytes)); 53 | values[i] = blob; 54 | size += byteListLengthEncodedSize(blob.size()); 55 | } else { 56 | values[i] = EMPTY_BLOB; 57 | size += 1; 58 | } 59 | } 60 | return size; 61 | } 62 | 63 | public List toList() { 64 | List l = new ArrayList<>(values.length); 65 | for (OptionalBlob value : values) { 66 | if (value.hasItem()) { 67 | l.add(string(value.getItem())); 68 | } else { 69 | l.add(null); 70 | } 71 | } 72 | return l; 73 | } 74 | 75 | private static String string(ByteBuffer b) { 76 | return new String(b.array(), b.arrayOffset() + b.position(), b.remaining(), StandardCharsets.UTF_8); 77 | } 78 | 79 | public int elementCount() { 80 | return values.length; 81 | } 82 | 83 | @Override 84 | public void decodeFrom(SparrowhawkDeserializer d) { 85 | int count = decodeLenPrefixedListLengthChecked(d.varUL()); 86 | if (count <= 0) { 87 | values = EMPTY; 88 | size = 0; 89 | return; 90 | } 91 | 92 | values = new OptionalBlob[count]; 93 | for (int i = 0; i < count; i++) { 94 | OptionalBlob blob = new OptionalBlob(); 95 | blob.decodeFrom(d); 96 | values[i] = blob; 97 | } 98 | } 99 | 100 | @Override 101 | public void encodeTo(SparrowhawkSerializer s) { 102 | int count = values.length; 103 | s.writeVarUL(encodeLenPrefixedListLength(count)); 104 | for (int i = 0; i < count; i++) { 105 | values[i].encodeTo(s); 106 | } 107 | } 108 | 109 | @Override 110 | public int size() { 111 | int size = this.size; 112 | if (size >= 0) { 113 | return size; 114 | } 115 | for (OptionalBlob blob : values) { 116 | size += byteListLengthEncodedSize(blob.size()); 117 | } 118 | this.size = size; 119 | return size; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/OptionalObject.java: -------------------------------------------------------------------------------- 1 | package software.amazon.smithy.java.sparrowhawk; 2 | 3 | import static software.amazon.smithy.java.sparrowhawk.KConstants.T_LIST; 4 | import static software.amazon.smithy.java.sparrowhawk.KConstants.decodeElementCount; 5 | import static software.amazon.smithy.java.sparrowhawk.KConstants.encodeByteListLength; 6 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.byteListLengthEncodedSize; 7 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.ulongSize; 8 | 9 | import java.nio.ByteBuffer; 10 | import java.util.Objects; 11 | import java.util.function.Supplier; 12 | 13 | 14 | @SuppressWarnings("unchecked") 15 | final class OptionalObject implements SparrowhawkObject { 16 | private static final long REQUIRED_LIST_0 = 0x0L; 17 | private long $list_0 = REQUIRED_LIST_0; 18 | // list fieldSet 0 index 1 19 | private static final long FIELD_ITEM = 0x8L; 20 | private Object object; 21 | 22 | private final Supplier factory; 23 | 24 | public OptionalObject(Supplier factory) { 25 | this.factory = factory; 26 | } 27 | 28 | public T getItem() { 29 | if (object instanceof ByteBuffer b) { 30 | SparrowhawkDeserializer deserializer = new SparrowhawkDeserializer(b); 31 | T obj = factory.get(); 32 | obj.decodeFrom(deserializer); 33 | this.object = obj; 34 | } 35 | return (T) object; 36 | } 37 | 38 | public void setItem(T object) { 39 | if (object == null) { 40 | $list_0 &= ~FIELD_ITEM; 41 | } else { 42 | $list_0 |= FIELD_ITEM; 43 | } 44 | this.object = object; 45 | this.$size = -1; 46 | } 47 | 48 | public boolean hasItem() { 49 | return ($list_0 & FIELD_ITEM) != 0; 50 | } 51 | 52 | private int $size; 53 | 54 | public int size() { 55 | if ($size >= 0) { 56 | return $size; 57 | } 58 | 59 | int size = ($list_0 == 0x0L ? 0 : (ulongSize($list_0))); 60 | size += sizeListFields(); 61 | this.$size = size; 62 | return size; 63 | } 64 | 65 | private int sizeListFields() { 66 | int size = 0; 67 | if (hasItem()) { 68 | if (object instanceof SparrowhawkObject) { 69 | size += byteListLengthEncodedSize(((T) object).size()); 70 | } else { 71 | size += ((ByteBuffer) object).remaining(); 72 | } 73 | } 74 | return size; 75 | } 76 | 77 | public void encodeTo(SparrowhawkSerializer s) { 78 | s.writeVarUL(encodeByteListLength(size())); 79 | writeListFields(s); 80 | } 81 | 82 | private void writeListFields(SparrowhawkSerializer s) { 83 | if ($list_0 != 0x0L) { 84 | s.writeVarUL($list_0); 85 | if (hasItem()) { 86 | if (object instanceof SparrowhawkObject k) { 87 | k.encodeTo(s); 88 | } else { 89 | s.writeEncodedObject((ByteBuffer) object); 90 | } 91 | } 92 | } 93 | } 94 | 95 | public void decodeFrom(SparrowhawkDeserializer d) { 96 | int size = (int) decodeElementCount(d.varUI()); 97 | this.$size = size; 98 | int start = d.pos(); 99 | 100 | while ((d.pos() - start) < size) { 101 | long fieldSet = d.varUL(); 102 | int fieldSetIdx = ((fieldSet & 0b100) != 0) ? d.varUI() + 1 : 0; 103 | int type = (int) (fieldSet & 3); 104 | if (type == T_LIST) { 105 | if (fieldSetIdx != 0) { throw new IllegalArgumentException("unknown fieldSetIdx " + fieldSetIdx); } 106 | decodeListFieldSet0(d, fieldSet); 107 | } else { 108 | throw new RuntimeException("Unexpected field set type: " + type); 109 | } 110 | } 111 | } 112 | 113 | private void decodeListFieldSet0(SparrowhawkDeserializer d, long fieldSet) { 114 | this.$list_0 = fieldSet; 115 | if (hasItem()) { 116 | this.object = d.object(); 117 | } 118 | } 119 | 120 | @Override 121 | public boolean equals(Object other) { 122 | if (this == other) return true; 123 | if (!(other instanceof OptionalObject o)) return false; 124 | return Objects.equals(getItem(), o.getItem()); 125 | } 126 | 127 | @Override 128 | public int hashCode() { 129 | return Objects.hash(getItem()); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/KConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | public final class KConstants { 9 | public static final int T_LIST = 0; 10 | public static final int T_VARINT = 1; 11 | public static final int T_FOUR = 2; 12 | public static final int T_EIGHT = 3; 13 | 14 | public static final int LIST_BYTES = 0; 15 | public static final int LIST_LEN_DELIMITED_ITEMS = 0b001; 16 | public static final int LIST_VARINTS = 0b011; 17 | public static final int LIST_FOUR = 0b101; 18 | public static final int LIST_EIGHT = 0b111; 19 | 20 | public static String fieldType(long fieldset) { 21 | switch ((int) (fieldset & 3)) { 22 | case 0: 23 | return "T_LIST"; 24 | case 1: 25 | return "T_VARINT"; 26 | case 2: 27 | return "T_FOUR"; 28 | case 3: 29 | return "T_EIGHT"; 30 | default: 31 | throw new IllegalStateException("impossible"); 32 | } 33 | } 34 | 35 | public static long varintField(long fields) { 36 | return (fields << 3) + T_VARINT; 37 | } 38 | 39 | public static long fourField(long fields) { 40 | return (fields << 3) + T_FOUR; 41 | } 42 | 43 | public static long eightField(long fields) { 44 | return (fields << 3) + T_EIGHT; 45 | } 46 | 47 | public static long listField(long fields) { 48 | return (fields << 3); 49 | } 50 | 51 | public static String listType(int len) { 52 | if (isByteListLength(len)) return "byte"; 53 | int lt = len & 7; 54 | switch (lt) { 55 | case LIST_LEN_DELIMITED_ITEMS: 56 | return "length-delimited items"; 57 | case LIST_VARINTS: 58 | return "varints"; 59 | case LIST_FOUR: 60 | return "four-byte elements"; 61 | case LIST_EIGHT: 62 | return "eight-byte elements"; 63 | default: 64 | throw new IllegalArgumentException("invalid list type: " + lt); 65 | } 66 | } 67 | 68 | public static long encodeByteListLength(long l) { 69 | return l << 1; 70 | } 71 | 72 | public static int encodeLenPrefixedListLength(int l) { 73 | return (l << 3) + LIST_LEN_DELIMITED_ITEMS; 74 | } 75 | 76 | public static int encodeVarintListLength(int l) { 77 | return (l << 3) + LIST_VARINTS; 78 | } 79 | 80 | public static long encodeFourBListLength(long l) { 81 | return (l << 3) + LIST_FOUR; 82 | } 83 | 84 | public static long encodeEightBListLength(long l) { 85 | return (l << 3) + LIST_EIGHT; 86 | } 87 | 88 | public static boolean isByteListLength(long l) { 89 | return (l & 1) == 0; 90 | } 91 | 92 | public static long decodeElementCount(long l) { 93 | return l >> (1 + (2 * (l & 1))); 94 | // if ((l & 1) == 0) return l >> 1; 95 | // return l >> 3; 96 | } 97 | 98 | public static int decodeByteListLength(long l) { 99 | return (int) (l >> 1); 100 | } 101 | 102 | public static int decodeByteListLengthChecked(long len) { 103 | long type = len & 1; 104 | if (type != LIST_BYTES) { 105 | badList("bytes", type); 106 | } 107 | return (int) len >> 1; 108 | } 109 | 110 | public static int decodeLenPrefixedListLengthChecked(long len) { 111 | long type = len & 7; 112 | if (type != LIST_LEN_DELIMITED_ITEMS) { 113 | badList("length-delimited", type); 114 | } 115 | return (int) len >> 3; 116 | } 117 | 118 | public static int decodeVarintListLengthChecked(long len) { 119 | long type = len & 7; 120 | if (type != LIST_VARINTS) { 121 | badList("varint", type); 122 | } 123 | return (int) len >> 3; 124 | } 125 | 126 | public static int decodeFourByteListLengthChecked(long len) { 127 | long type = len & 7; 128 | if (type != LIST_FOUR) { 129 | badList("four-byte", type); 130 | } 131 | return (int) len >> 3; 132 | } 133 | 134 | public static int decodeEightByteListLengthChecked(long len) { 135 | long type = len & 7; 136 | if (type != LIST_EIGHT) { 137 | badList("eight-byte", type); 138 | } 139 | return (int) len >> 3; 140 | } 141 | 142 | private static void badList(String expected, long type) { 143 | throw new RuntimeException( 144 | "expected list of " + expected + " items, got: " 145 | + listType((int) type) 146 | ); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/JavaImportContainer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import java.util.Comparator; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.TreeMap; 12 | import software.amazon.smithy.codegen.core.ImportContainer; 13 | import software.amazon.smithy.codegen.core.Symbol; 14 | import software.amazon.smithy.codegen.core.SymbolReference; 15 | 16 | final class JavaImportContainer implements ImportContainer { 17 | 18 | private final String packageName; 19 | private final String header; 20 | private final Map aliasesToSymbol = new HashMap<>(); 21 | private final Map symbolsToAlias = new TreeMap<>( 22 | Comparator.comparing(Symbol::getNamespace).thenComparing(Symbol::getName) 23 | ); 24 | 25 | private final Map staticAliasesToSymbol = new HashMap<>(); 26 | private final Map staticSymbolsToAlias = new TreeMap<>( 27 | Comparator.comparing(Symbol::getNamespace).thenComparing(Symbol::getName) 28 | ); 29 | 30 | 31 | JavaImportContainer(String packageName, String header) { 32 | this.packageName = packageName; 33 | this.header = header; 34 | } 35 | 36 | @Override 37 | public void importSymbol(Symbol symbol, String alias) { 38 | importSymbol(symbol); 39 | } 40 | 41 | @Override 42 | public void importSymbol(Symbol symbol) { 43 | if (symbolsToAlias.containsKey(symbol)) { 44 | return; 45 | } 46 | 47 | if (symbol.getNamespace() == null || symbol.getNamespace().isEmpty()) { 48 | symbolsToAlias.put(symbol, symbol.getName()); 49 | aliasesToSymbol.put(symbol.getName(), symbol); 50 | return; 51 | } 52 | 53 | if (!aliasesToSymbol.containsKey(symbol.getName())) { 54 | aliasesToSymbol.put(symbol.getName(), symbol); 55 | symbolsToAlias.put(symbol, symbol.getName()); 56 | } else { 57 | symbolsToAlias.put(symbol, symbol.getNamespace() + "." + symbol.getName()); 58 | aliasesToSymbol.put(symbol.getNamespace() + "." + symbol.getName(), symbol); 59 | } 60 | } 61 | 62 | public void importReference(SymbolReference reference) { 63 | var refSymbol = reference.getSymbol(); 64 | if (!reference.getOptions().contains(CommonSymbols.UseOption.STATIC)) { 65 | importSymbol(refSymbol); 66 | } else { 67 | if (staticSymbolsToAlias.containsKey(refSymbol)) { 68 | return; 69 | } 70 | if (staticAliasesToSymbol.containsKey(refSymbol.getName())) { 71 | return; 72 | } 73 | staticSymbolsToAlias.put(refSymbol, refSymbol.getName()); 74 | staticAliasesToSymbol.put(refSymbol.getName(), refSymbol); 75 | } 76 | } 77 | 78 | public String getAlias(Symbol symbol) { 79 | return symbolsToAlias.get(symbol); 80 | } 81 | 82 | public String getAlias(SymbolReference ref) { 83 | Symbol refSymbol = ref.getSymbol(); 84 | if (ref.getOptions().contains(CommonSymbols.UseOption.STATIC)) { 85 | var alias = staticSymbolsToAlias.get(refSymbol); 86 | if (alias == null) { 87 | alias = refSymbol.getNamespace() + "." + refSymbol.getName(); 88 | } 89 | return alias; 90 | } 91 | return getAlias(refSymbol); 92 | } 93 | 94 | @Override 95 | public String toString() { 96 | StringBuilder sb = new StringBuilder(); 97 | if (header != null) { 98 | sb.append(header); 99 | sb.append('\n'); 100 | } 101 | 102 | sb.append("package ") 103 | .append(packageName) 104 | .append(";\n\n"); 105 | 106 | for (Map.Entry e : symbolsToAlias.entrySet()) { 107 | if (packageName.equals(e.getKey().getNamespace()) 108 | || e.getKey().getNamespace() == null 109 | || e.getKey().getNamespace().isEmpty()) { 110 | continue; 111 | } 112 | sb.append("import ") 113 | .append(e.getKey().getNamespace()) 114 | .append('.') 115 | .append(e.getKey().getName()) 116 | .append(";\n"); 117 | } 118 | if (!staticSymbolsToAlias.isEmpty()) { 119 | sb.append("\n"); 120 | for (Map.Entry e : staticSymbolsToAlias.entrySet()) { 121 | sb.append("import static ") 122 | .append(e.getKey().getNamespace()) 123 | .append('.') 124 | .append(e.getKey().getName()) 125 | .append(";\n"); 126 | } 127 | } 128 | return sb.toString(); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /sparrowhawk-traits/src/main/java/software/amazon/smithy/protocol/traits/IndexedServiceTraitValidator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.protocol.traits; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Comparator; 10 | import java.util.HashSet; 11 | import java.util.List; 12 | import java.util.Set; 13 | import software.amazon.smithy.model.Model; 14 | import software.amazon.smithy.model.neighbor.Walker; 15 | import software.amazon.smithy.model.shapes.MemberShape; 16 | import software.amazon.smithy.model.shapes.ServiceShape; 17 | import software.amazon.smithy.model.shapes.Shape; 18 | import software.amazon.smithy.model.shapes.ShapeId; 19 | import software.amazon.smithy.model.traits.EventHeaderTrait; 20 | import software.amazon.smithy.model.traits.EventPayloadTrait; 21 | import software.amazon.smithy.model.traits.MixinTrait; 22 | import software.amazon.smithy.model.traits.StreamingTrait; 23 | import software.amazon.smithy.model.validation.AbstractValidator; 24 | import software.amazon.smithy.model.validation.ValidationEvent; 25 | 26 | /** 27 | * A validator for indexed services. If a shape is in the closure of an indexed service, 28 | * its members must be indexed, the ordering starts at 1 and has no gaps. 29 | */ 30 | public final class IndexedServiceTraitValidator extends AbstractValidator { 31 | 32 | @Override 33 | public List validate(Model model) { 34 | List events = new ArrayList<>(); 35 | 36 | Set shapesOfInterest = new HashSet<>(); 37 | Set services = model.getServiceShapesWithTrait(IndexedServiceTrait.class); 38 | for (ServiceShape service : services) { 39 | new Walker(model).iterateShapes(service).forEachRemaining(shape -> { 40 | if ((shape.isStructureShape() || shape.isUnionShape()) 41 | && !shape.hasTrait(MixinTrait.class)) { 42 | shapesOfInterest.add(shape.getId()); 43 | } 44 | }); 45 | } 46 | 47 | for (ShapeId shapeId : shapesOfInterest) { 48 | validateMembers(model, model.expectShape(shapeId), events); 49 | } 50 | 51 | return events; 52 | } 53 | 54 | private void validateMembers( 55 | Model model, 56 | Shape container, 57 | List events 58 | ) { 59 | int lastIdx = 0; 60 | List sorted = new ArrayList<>(container.members()); 61 | sorted.sort(Comparator.comparingInt(t -> t.getTrait(IdxTrait.class).map(IdxTrait::getValue).orElse(-1))); 62 | 63 | for (MemberShape ms : sorted) { 64 | if (!ms.hasTrait(IdxTrait.class)) { 65 | if (ms.hasTrait(EventHeaderTrait.class) || ms.hasTrait(EventPayloadTrait.class)) { 66 | continue; 67 | } 68 | Shape untraitedTarget = model.expectShape(ms.getTarget()); 69 | if ((untraitedTarget.isBlobShape() || untraitedTarget.isUnionShape()) 70 | && untraitedTarget.hasTrait(StreamingTrait.class)) { 71 | continue; 72 | } 73 | 74 | events.add( 75 | error( 76 | ms, 77 | ms.getSourceLocation(), 78 | String.format( 79 | "Structure \"%s\" is in the closure of an indexed service, but its member " 80 | + "\"%s\" is missing an idx trait", 81 | ms.getContainer(), 82 | ms.toShapeId() 83 | ) 84 | ) 85 | ); 86 | 87 | continue; 88 | } 89 | 90 | IdxTrait trait = ms.expectTrait(IdxTrait.class); 91 | if (trait.getValue() == lastIdx) { 92 | events.add(error(ms, trait, String.format("Duplicate idx value \"%d\"", lastIdx))); 93 | } else if (trait.getValue() != ++lastIdx) { 94 | if (trait.getValue() - 1 == lastIdx) { 95 | events.add( 96 | error( 97 | ms, 98 | trait, 99 | String.format( 100 | "idx must increase monotonically starting at 1, no members found for idx " 101 | + "%d", 102 | lastIdx 103 | ) 104 | ) 105 | ); 106 | 107 | } else { 108 | events.add( 109 | error( 110 | ms, 111 | trait, 112 | String.format( 113 | "idx must increase monotonically starting at 1, no members found for idxs " 114 | + "between %d and %d, inclusive", 115 | lastIdx, 116 | trait.getValue() - 1 117 | ) 118 | ) 119 | ); 120 | 121 | } 122 | lastIdx = trait.getValue(); 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /sparrowhawk-traits/src/main/java/software/amazon/smithy/protocol/traits/Rpcv2SparrowhawkTrait.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.protocol.traits; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import software.amazon.smithy.model.node.Node; 11 | import software.amazon.smithy.model.node.ObjectNode; 12 | import software.amazon.smithy.model.shapes.ShapeId; 13 | import software.amazon.smithy.model.traits.AbstractTrait; 14 | import software.amazon.smithy.model.traits.AbstractTraitBuilder; 15 | import software.amazon.smithy.model.traits.Trait; 16 | import software.amazon.smithy.utils.ToSmithyBuilder; 17 | 18 | public final class Rpcv2SparrowhawkTrait extends AbstractTrait implements ToSmithyBuilder { 19 | 20 | public static final ShapeId ID = ShapeId.from("smithy.protocols#rpcv2Sparrowhawk"); 21 | 22 | private static final String HTTP = "http"; 23 | private static final String EVENT_STREAM_HTTP = "eventStreamHttp"; 24 | 25 | private final List http; 26 | private final List eventStreamHttp; 27 | 28 | private Rpcv2SparrowhawkTrait(Builder builder) { 29 | super(ID, builder.getSourceLocation()); 30 | http = List.copyOf(builder.http); 31 | eventStreamHttp = List.copyOf(builder.eventStreamHttp); 32 | } 33 | 34 | /** 35 | * Creates a new {@code Builder}. 36 | */ 37 | public static Builder builder() { 38 | return new Builder(); 39 | } 40 | 41 | /** 42 | * Updates the builder from a Node. 43 | * 44 | * @param node Node object that must be a valid {@code ObjectNode}. 45 | * @return Returns the updated builder. 46 | */ 47 | public static Rpcv2SparrowhawkTrait fromNode(Node node) { 48 | Builder builder = builder().sourceLocation(node); 49 | ObjectNode objectNode = node.expectObjectNode(); 50 | objectNode.getArrayMember(HTTP) 51 | .map(values -> Node.loadArrayOfString(HTTP, values)) 52 | .ifPresent(builder::http); 53 | objectNode.getArrayMember(EVENT_STREAM_HTTP) 54 | .map(values -> Node.loadArrayOfString(EVENT_STREAM_HTTP, values)) 55 | .ifPresent(builder::eventStreamHttp); 56 | return builder.build(); 57 | } 58 | 59 | /** 60 | * Gets the priority ordered list of supported HTTP protocol versions. 61 | * 62 | * @return Returns the supported HTTP protocol versions. 63 | */ 64 | public List getHttp() { 65 | return http; 66 | } 67 | 68 | /** 69 | * Gets the priority ordered list of supported HTTP protocol versions that are required when 70 | * using event streams. 71 | * 72 | * @return Returns the supported event stream HTTP protocol versions. 73 | */ 74 | public List getEventStreamHttp() { 75 | return eventStreamHttp; 76 | } 77 | 78 | @Override 79 | protected Node createNode() { 80 | ObjectNode.Builder builder = Node.objectNodeBuilder().sourceLocation(getSourceLocation()); 81 | if (!getHttp().isEmpty()) { 82 | builder.withMember(HTTP, Node.fromStrings(getHttp())); 83 | } 84 | if (!getEventStreamHttp().isEmpty()) { 85 | builder.withMember(EVENT_STREAM_HTTP, Node.fromStrings(getEventStreamHttp())); 86 | } 87 | return builder.build(); 88 | } 89 | 90 | @Override 91 | public Builder toBuilder() { 92 | return builder().http(http).eventStreamHttp(eventStreamHttp); 93 | } 94 | 95 | /** 96 | * Builder for creating a {@code Rpcv2CborTrait}. 97 | */ 98 | public static final class Builder extends AbstractTraitBuilder { 99 | 100 | private final List http = new ArrayList<>(); 101 | private final List eventStreamHttp = new ArrayList<>(); 102 | 103 | @Override 104 | public Rpcv2SparrowhawkTrait build() { 105 | return new Rpcv2SparrowhawkTrait(this); 106 | } 107 | 108 | /** 109 | * Sets the list of supported HTTP protocols. 110 | * 111 | * @param http HTTP protocols to set and replace. 112 | * @return Returns the builder. 113 | */ 114 | public Builder http(List http) { 115 | this.http.clear(); 116 | this.http.addAll(http); 117 | return this; 118 | } 119 | 120 | /** 121 | * Sets the list of supported event stream HTTP protocols. 122 | * 123 | * @param eventStreamHttp Event stream HTTP protocols to set and replace. 124 | * @return Returns the builder. 125 | */ 126 | public Builder eventStreamHttp(List eventStreamHttp) { 127 | this.eventStreamHttp.clear(); 128 | this.eventStreamHttp.addAll(eventStreamHttp); 129 | return this; 130 | } 131 | } 132 | 133 | /** 134 | * Implements the {@code AbstractTrait.Provider}. 135 | */ 136 | public static final class Provider extends AbstractTrait.Provider { 137 | 138 | public Provider() { 139 | super(ID); 140 | } 141 | 142 | @Override 143 | public Trait createTrait(ShapeId target, Node value) { 144 | Rpcv2SparrowhawkTrait result = fromNode(value); 145 | result.setNodeCache(value); 146 | return result; 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/NumberMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.*; 9 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.*; 10 | 11 | import java.nio.ByteBuffer; 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | @SuppressWarnings("unchecked") 17 | public abstract class NumberMap implements SparrowhawkObject { 18 | private static final long REQUIRED_LIST_FIELDSET_0 = KConstants.listField(0b11); 19 | private static final ByteBuffer[] EMPTY_KEYS = new ByteBuffer[0]; 20 | private static final Object[] EMPTY_VALUES = new Object[0]; 21 | 22 | private ByteBuffer[] keys; 23 | private T[] values; 24 | 25 | public final Map toMap() { 26 | int sz = keys.length; 27 | Map m = new HashMap<>(sz / 3 * 4); 28 | for (int i = 0; i < sz; i++) { 29 | m.put(string(keys[i]), values[i]); 30 | } 31 | return m; 32 | } 33 | 34 | private static String string(ByteBuffer b) { 35 | return new String(b.array(), b.arrayOffset() + b.position(), b.remaining(), StandardCharsets.UTF_8); 36 | } 37 | 38 | public final void fromMap(Map map) { 39 | int len = map.size(); 40 | if (len == 0) { 41 | keys = EMPTY_KEYS; 42 | values = (T[]) EMPTY_VALUES; 43 | $size = 0; 44 | return; 45 | } 46 | 47 | ByteBuffer[] keys = new ByteBuffer[len]; 48 | this.keys = keys; 49 | T[] values = newArray(len); 50 | this.values = values; 51 | int i = 0; 52 | for (Map.Entry entry : map.entrySet()) { 53 | byte[] key = entry.getKey().getBytes(StandardCharsets.UTF_8); 54 | keys[i] = ByteBuffer.wrap(key); 55 | T value = entry.getValue(); 56 | values[i++] = value; 57 | } 58 | this.$size = -1; 59 | } 60 | 61 | protected abstract T[] newArray(int len); 62 | 63 | @Override 64 | public final void decodeFrom(SparrowhawkDeserializer d) { 65 | int size = (int) decodeElementCount(d.varUI()); 66 | $size = size; 67 | if (size > 0) { 68 | long fieldset = d.varUL(); 69 | if ((fieldset & 3) != KConstants.T_LIST) { 70 | throw new RuntimeException("bad field type: " + KConstants.fieldType(fieldset)); 71 | } 72 | if ((fieldset & REQUIRED_LIST_FIELDSET_0) != REQUIRED_LIST_FIELDSET_0) { 73 | throw new RuntimeException("missing required fields"); 74 | } 75 | int nkeys = (int) decodeElementCount(d.varUI()); 76 | keys = readKeys(d, nkeys); 77 | int nvalues = decodeValueCount(d.varUI()); 78 | if (nkeys != nvalues) { 79 | throw new RuntimeException("mismatch in key and value lengths"); 80 | } 81 | values = readValues(d, nvalues); 82 | } else { 83 | keys = EMPTY_KEYS; 84 | values = (T[]) EMPTY_VALUES; 85 | } 86 | } 87 | 88 | private static ByteBuffer[] readKeys(SparrowhawkDeserializer d, int n) { 89 | ByteBuffer[] bs = new ByteBuffer[n]; 90 | for (int i = 0; i < bs.length; i++) { 91 | bs[i] = d.bytes(); 92 | } 93 | return bs; 94 | } 95 | 96 | private T[] readValues(SparrowhawkDeserializer d, int n) { 97 | T[] values = newArray(n); 98 | for (int i = 0; i < values.length; i++) { 99 | values[i] = decode(d); 100 | } 101 | return values; 102 | } 103 | 104 | protected abstract int decodeValueCount(int encodedCount); 105 | 106 | protected abstract T decode(SparrowhawkDeserializer d); 107 | 108 | @Override 109 | public final void encodeTo(SparrowhawkSerializer s) { 110 | int size = size(); 111 | if (size > 0) { 112 | s.writeVarUL(encodeByteListLength(size)); 113 | s.writeVarUL(REQUIRED_LIST_FIELDSET_0); 114 | long dl = encodeLenPrefixedListLength(keys.length); 115 | s.writeVarUL(dl); 116 | for (int i = 0; i < keys.length; i++) { 117 | s.writeBytes(keys[i]); 118 | } 119 | writeValues(s, values); 120 | } else { 121 | s.writeRawByte(EMPTY_LIST_SIZE_VARINT); 122 | } 123 | } 124 | 125 | protected abstract void writeValues(SparrowhawkSerializer s, T[] values); 126 | 127 | 128 | private transient int $size; 129 | 130 | protected abstract int sizeofValues(T[] elements); 131 | 132 | @Override 133 | public final int size() { 134 | int size = $size; 135 | if (size >= 0) { 136 | return size; 137 | } 138 | 139 | if (keys.length != values.length) { 140 | return invalidMap(); 141 | } 142 | 143 | if (keys.length > 0) { 144 | size = 1; // required list field 0 145 | size += ulongSize(encodeLenPrefixedListLength(keys.length)); 146 | for (int i = 0; i < keys.length; i++) { 147 | size += byteListLengthEncodedSize(keys[i].remaining()); 148 | } 149 | size += sizeofValues(values); 150 | } else { 151 | size = 0; 152 | } 153 | 154 | this.$size = size; 155 | return size; 156 | } 157 | 158 | private static int invalidMap() { 159 | throw new RuntimeException("invalid map"); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/SparrowhawkIndex.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import java.util.*; 9 | import software.amazon.smithy.model.Model; 10 | import software.amazon.smithy.model.knowledge.KnowledgeIndex; 11 | import software.amazon.smithy.model.shapes.*; 12 | import software.amazon.smithy.model.traits.StreamingTrait; 13 | 14 | public final class SparrowhawkIndex implements KnowledgeIndex { 15 | private final Map>> varintMembers = new HashMap<>(); 16 | private final Map>> fourByteMembers = new HashMap<>(); 17 | private final Map>> eightByteMembers = new HashMap<>(); 18 | private final Map>> listMembers = new HashMap<>(); 19 | 20 | public SparrowhawkIndex(Model model) { 21 | for (StructureShape structureShape : model.getStructureShapes()) { 22 | index(structureShape, model); 23 | } 24 | for (UnionShape unionShape : model.getUnionShapes()) { 25 | index(unionShape, model); 26 | } 27 | } 28 | 29 | public static SparrowhawkIndex of(Model model) { 30 | return model.getKnowledge(SparrowhawkIndex.class, SparrowhawkIndex::new); 31 | } 32 | 33 | private void index(Shape shape, Model model) { 34 | List members = new ArrayList<>(shape.members()); 35 | if (members.isEmpty() || !members.stream().findFirst().get().hasTrait(SparrowhawkFieldTrait.class)) { 36 | return; 37 | } 38 | 39 | members.sort( 40 | Comparator.comparing((MemberShape m) -> m.expectTrait(SparrowhawkFieldTrait.class).getType()) 41 | .thenComparing(m -> m.expectTrait(SparrowhawkFieldTrait.class).getFieldSetIdx()) 42 | .thenComparing(m -> m.expectTrait(SparrowhawkFieldTrait.class).getTypeIdx()) 43 | ); 44 | 45 | for (MemberShape ms : members) { 46 | if (model.expectShape(ms.getTarget()).hasTrait(StreamingTrait.class)) { 47 | continue; 48 | } 49 | 50 | SparrowhawkFieldTrait trait = ms.expectTrait(SparrowhawkFieldTrait.class); 51 | (switch (trait.getType()) { 52 | case VARINT -> varintMembers; 53 | case LIST -> listMembers; 54 | case FOUR_BYTE -> fourByteMembers; 55 | case EIGHT_BYTE -> eightByteMembers; 56 | }).computeIfAbsent(shape.toShapeId(), $ -> new HashMap<>()) 57 | .computeIfAbsent(trait.getFieldSetIdx(), $ -> new ArrayList<>()) 58 | .add(ms); 59 | } 60 | } 61 | 62 | public int getVarintFieldSetCount(ToShapeId shape) { 63 | return varintMembers.getOrDefault(shape.toShapeId(), Collections.emptyMap()).size(); 64 | } 65 | 66 | public boolean hasVarintMembers(ToShapeId shape) { 67 | return !varintMembers.getOrDefault(shape.toShapeId(), Collections.emptyMap()).isEmpty(); 68 | } 69 | 70 | public List getVarintMembers(ToShapeId shape, int fieldSetIdx) { 71 | return Collections.unmodifiableList( 72 | varintMembers.getOrDefault(shape.toShapeId(), Collections.emptyMap()) 73 | .getOrDefault(fieldSetIdx, Collections.emptyList()) 74 | ); 75 | } 76 | 77 | public int getFourByteFieldSetCount(ToShapeId shape) { 78 | return fourByteMembers.getOrDefault(shape.toShapeId(), Collections.emptyMap()).size(); 79 | } 80 | 81 | public boolean hasFourByteMembers(ToShapeId shape) { 82 | return !fourByteMembers.getOrDefault(shape.toShapeId(), Collections.emptyMap()).isEmpty(); 83 | } 84 | 85 | public List getFourByteMembers(ToShapeId shape, int fieldSetIdx) { 86 | return Collections.unmodifiableList( 87 | fourByteMembers.getOrDefault(shape.toShapeId(), Collections.emptyMap()) 88 | .getOrDefault(fieldSetIdx, Collections.emptyList()) 89 | ); 90 | } 91 | 92 | public int getEightByteFieldSetCount(ToShapeId shape) { 93 | return eightByteMembers.getOrDefault(shape.toShapeId(), Collections.emptyMap()).size(); 94 | } 95 | 96 | public boolean hasEightByteMembers(ToShapeId shape) { 97 | return !eightByteMembers.getOrDefault(shape.toShapeId(), Collections.emptyMap()).isEmpty(); 98 | } 99 | 100 | public List getEightByteMembers(ToShapeId shape, int fieldSetIdx) { 101 | return Collections.unmodifiableList( 102 | eightByteMembers.getOrDefault(shape.toShapeId(), Collections.emptyMap()) 103 | .getOrDefault(fieldSetIdx, Collections.emptyList()) 104 | ); 105 | } 106 | 107 | public int getListFieldSetCount(ToShapeId shape) { 108 | return listMembers.getOrDefault(shape.toShapeId(), Collections.emptyMap()).size(); 109 | } 110 | 111 | public boolean hasListMembers(ToShapeId shape) { 112 | return !listMembers.getOrDefault(shape.toShapeId(), Collections.emptyMap()).isEmpty(); 113 | } 114 | 115 | public boolean hasRequiredLists(ToShapeId shape) { 116 | return listMembers.getOrDefault(shape.toShapeId(), Collections.emptyMap()) 117 | .values() 118 | .stream() 119 | .flatMap(Collection::stream) 120 | .anyMatch(ms -> ms.expectTrait(SparrowhawkFieldTrait.class).isRequired()); 121 | } 122 | 123 | public List getListMembers(ToShapeId shape, int fieldSetIdx) { 124 | return Collections.unmodifiableList( 125 | listMembers.getOrDefault(shape.toShapeId(), Collections.emptyMap()) 126 | .getOrDefault(fieldSetIdx, Collections.emptyList()) 127 | ); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/test/java/software/amazon/smithy/java/sparrowhawk/RoundtripTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | 10 | import java.util.*; 11 | import java.util.function.IntFunction; 12 | import java.util.function.Supplier; 13 | import java.util.stream.Stream; 14 | import org.junit.jupiter.api.Test; 15 | import org.junit.jupiter.params.ParameterizedTest; 16 | import org.junit.jupiter.params.provider.Arguments; 17 | import org.junit.jupiter.params.provider.MethodSource; 18 | import org.junit.jupiter.params.provider.ValueSource; 19 | 20 | public class RoundtripTests { 21 | @MethodSource("maps") 22 | @ParameterizedTest(name = "{0}") 23 | public void smallMaps(String name, Object n, Supplier supp) { 24 | runTest(generateMap(n, 1), supp); 25 | } 26 | 27 | @MethodSource("maps") 28 | @ParameterizedTest(name = "{0}") 29 | public void bigMaps(String name, Object n, Supplier supp) { 30 | runTest(generateMap(n, 10000), supp); 31 | } 32 | 33 | public static Stream maps() { 34 | return Stream.of( 35 | Arguments.of("BooleanMap", false, (Supplier) BooleanMap::new), 36 | Arguments.of("ByteMap", (byte) 4, (Supplier) ByteMap::new), 37 | Arguments.of("ShortMap", (short) 4, (Supplier) ShortMap::new), 38 | Arguments.of("IntegerMap", 123, (Supplier) IntegerMap::new), 39 | Arguments.of("LongMap", 9199192222L, (Supplier) LongMap::new), 40 | Arguments.of("FloatMap", 3.14f, (Supplier) FloatMap::new), 41 | Arguments.of("DoubleMap", 3.14d, (Supplier) DoubleMap::new) 42 | ); 43 | } 44 | 45 | @Test 46 | public void ints() { 47 | byte[] payload = new byte[100]; 48 | SparrowhawkSerializer s = new SparrowhawkSerializer(payload); 49 | List ints = Arrays.asList(0, 1, -1, 2, -2, Integer.MAX_VALUE, Integer.MIN_VALUE); 50 | ints.forEach(s::writeVarI); 51 | 52 | SparrowhawkDeserializer d = new SparrowhawkDeserializer(payload); 53 | for (int i : ints) { 54 | assertEquals(i, d.varI()); 55 | } 56 | } 57 | 58 | @Test 59 | public void longs() { 60 | byte[] payload = new byte[100]; 61 | SparrowhawkSerializer s = new SparrowhawkSerializer(payload); 62 | List longs = Arrays.asList(0L, 1L, -1L, 2L, -2L, Long.MIN_VALUE, Long.MAX_VALUE); 63 | longs.forEach(s::writeVarL); 64 | 65 | SparrowhawkDeserializer d = new SparrowhawkDeserializer(payload); 66 | for (long l : longs) { 67 | assertEquals(l, d.varL()); 68 | } 69 | } 70 | 71 | @ParameterizedTest 72 | @ValueSource(ints = {1, 10000}) 73 | public void stringMap(int len) { 74 | Map m = generateMap("hello", len); 75 | StringMap sm = new StringMap(); 76 | sm.fromMap(m); 77 | StringMap roundtrip = serde(sm, new StringMap()); 78 | assertEquals(m, roundtrip.toMap()); 79 | } 80 | 81 | @ParameterizedTest 82 | @ValueSource(ints = {1, 10000}) 83 | public void structureMap(int len) { 84 | Map map = generateMap(RoundtripTests::makeStruct, len); 85 | StructureMap m = new StructureMap<>(SparrowhawkCodegenOptionalStruct::new); 86 | m.fromMap(map); 87 | StructureMap roundtrip = serde( 88 | m, 89 | new StructureMap<>(SparrowhawkCodegenOptionalStruct::new) 90 | ); 91 | assertEquals(map, roundtrip.toMap()); 92 | } 93 | 94 | @ParameterizedTest 95 | @ValueSource(ints = {1, 10000}) 96 | public void stringList(int len) { 97 | List list = generateList(Integer::toString, len); 98 | StringList roundtrip = serde(StringList.fromList(list), new StringList()); 99 | assertEquals(list, roundtrip.toList()); 100 | } 101 | 102 | private static SparrowhawkCodegenOptionalStruct makeStruct() { 103 | SparrowhawkCodegenOptionalStruct struct = new SparrowhawkCodegenOptionalStruct(); 104 | struct.setString("hello"); 105 | struct.setTimestamp(123.456d); 106 | return struct; 107 | } 108 | 109 | private static void runTest(Map map, Supplier> supp) { 110 | NumberMap m = supp.get(); 111 | m.fromMap(map); 112 | NumberMap roundtrip = serde(m, supp.get()); 113 | assertEquals(map, roundtrip.toMap()); 114 | } 115 | 116 | private static T serde(T obj, T base) { 117 | byte[] ser = ser(obj); 118 | return de(base, ser); 119 | } 120 | 121 | private static byte[] ser(SparrowhawkObject o) { 122 | SparrowhawkSerializer s = new SparrowhawkSerializer(o.size()); 123 | o.encodeTo(s); 124 | return s.payload(); 125 | } 126 | 127 | private static T de(T base, byte[] payload) { 128 | SparrowhawkDeserializer d = new SparrowhawkDeserializer(payload); 129 | base.decodeFrom(d); 130 | d.done(); 131 | return base; 132 | } 133 | 134 | private static Map generateMap(T val, int count) { 135 | return generateMap(() -> val, count); 136 | } 137 | 138 | private static Map generateMap(Supplier val, int count) { 139 | Map map = new HashMap<>(count); 140 | for (int i = 0; i < count; i++) { 141 | map.put(Integer.toString(i), val.get()); 142 | } 143 | return map; 144 | } 145 | 146 | private static List generateList(IntFunction generator, int count) { 147 | List list = new ArrayList<>(count); 148 | for (int i = 0; i < count; i++) { 149 | list.add(generator.apply(i)); 150 | } 151 | return list; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/test/java/software/amazon/smithy/java/sparrowhawk/SparrowhawkCodegenOptionalStruct.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static java.nio.charset.StandardCharsets.UTF_8; 9 | import static software.amazon.smithy.java.sparrowhawk.KConstants.*; 10 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.*; 11 | 12 | import java.util.Objects; 13 | 14 | 15 | public final class SparrowhawkCodegenOptionalStruct implements SparrowhawkObject { 16 | private static final long REQUIRED_LIST_0 = 0x8L; 17 | private long $list_0 = REQUIRED_LIST_0; 18 | // list fieldSet 0 index 1 19 | private static final long FIELD_STRING = 0x8L; 20 | private Object string; 21 | 22 | public String getString() { 23 | if (string == null) { 24 | return null; 25 | } 26 | if (string instanceof String) { 27 | return (String) string; 28 | } 29 | String s = new String((byte[]) string, UTF_8); 30 | this.string = s; 31 | return s; 32 | } 33 | 34 | public void setString(String string) { 35 | if (string == null) { 36 | missingField("'string' is required"); 37 | } 38 | this.string = string; 39 | this.$size = 0; 40 | } 41 | 42 | public boolean hasString() { 43 | return ($list_0 & FIELD_STRING) != 0; 44 | } 45 | 46 | private static final long REQUIRED_EIGHT_BYTE_0 = 0xbL; 47 | private long $eightByte_0 = REQUIRED_EIGHT_BYTE_0; 48 | // eightByte fieldSet 0 index 1 49 | private static final long FIELD_TIMESTAMP = 0x8L; 50 | private double timestamp; 51 | 52 | public double getTimestamp() { 53 | return timestamp; 54 | } 55 | 56 | public void setTimestamp(double timestamp) { 57 | this.timestamp = timestamp; 58 | this.$size = 0; 59 | } 60 | 61 | public boolean hasTimestamp() { 62 | return ($eightByte_0 & FIELD_TIMESTAMP) != 0; 63 | } 64 | 65 | private int $size; 66 | 67 | public int size() { 68 | if ($size > 0) { 69 | return $size; 70 | } 71 | 72 | int size = ($list_0 == 0x0L ? 0 : (ulongSize($list_0))) + ($eightByte_0 == 0x3L 73 | ? 0 74 | : (ulongSize($eightByte_0))); 75 | size += sizeListFields(); 76 | size += sizeEightByteFields(); 77 | this.$size = size; 78 | return size; 79 | } 80 | 81 | private int sizeEightByteFields() { 82 | int size = 8; 83 | return size; 84 | } 85 | 86 | private int sizeListFields() { 87 | int size = 0; 88 | size += $stringLen(); 89 | return size; 90 | } 91 | 92 | private int $stringLen() { 93 | Object field = string; 94 | if (field == null) { 95 | missingField("Required field 'string' is missing"); 96 | } 97 | 98 | int size; 99 | if (field.getClass() == byte[].class) { 100 | size = ((byte[]) field).length; 101 | } else { 102 | byte[] bytes = ((String) field).getBytes(UTF_8); 103 | this.string = bytes; 104 | size = bytes.length; 105 | } 106 | 107 | return byteListLengthEncodedSize(size); 108 | } 109 | 110 | public void encodeTo(SparrowhawkSerializer s) { 111 | s.writeVarUL(encodeByteListLength(size())); 112 | writeEightByteFields(s); 113 | writeListFields(s); 114 | } 115 | 116 | private void writeEightByteFields(SparrowhawkSerializer s) { 117 | if ($eightByte_0 != 0x3L) { 118 | s.writeVarUL($eightByte_0); 119 | s.writeDouble(timestamp); 120 | } 121 | } 122 | 123 | private void writeListFields(SparrowhawkSerializer s) { 124 | if ($list_0 != 0x0L) { 125 | s.writeVarUL($list_0); 126 | s.writeBytes(string); 127 | } 128 | } 129 | 130 | public void decodeFrom(SparrowhawkDeserializer d) { 131 | int size = (int) decodeElementCount(d.varUI()); 132 | this.$size = size; 133 | int start = d.pos(); 134 | 135 | while ((d.pos() - start) < size) { 136 | long fieldSet = d.varUL(); 137 | int fieldSetIdx = ((fieldSet & 0b100) != 0) ? d.varUI() + 1 : 0; 138 | int type = (int) (fieldSet & 3); 139 | if (type == T_LIST) { 140 | if (fieldSetIdx != 0) { throw new IllegalArgumentException("unknown fieldSetIdx " + fieldSetIdx); } 141 | decodeListFieldSet0(d, fieldSet); 142 | } else if (type == T_EIGHT) { 143 | if (fieldSetIdx != 0) { throw new IllegalArgumentException("unknown fieldSetIdx " + fieldSetIdx); } 144 | decodeEightByteFieldSet0(d, fieldSet); 145 | } else { 146 | throw new RuntimeException("Unexpected field set type: " + type); 147 | } 148 | } 149 | } 150 | 151 | private void decodeEightByteFieldSet0(SparrowhawkDeserializer d, long fieldSet) { 152 | SparrowhawkDeserializer.checkFields(fieldSet, REQUIRED_EIGHT_BYTE_0, "eight-byte"); 153 | this.$eightByte_0 = fieldSet; 154 | this.timestamp = d.d8(); 155 | } 156 | 157 | private void decodeListFieldSet0(SparrowhawkDeserializer d, long fieldSet) { 158 | SparrowhawkDeserializer.checkFields(fieldSet, REQUIRED_LIST_0, "lists"); 159 | this.$list_0 = fieldSet; 160 | { 161 | this.string = d.string(); 162 | } 163 | } 164 | 165 | @Override 166 | public boolean equals(Object other) { 167 | if (this == other) return true; 168 | if (!(other instanceof SparrowhawkCodegenOptionalStruct)) return false; 169 | SparrowhawkCodegenOptionalStruct o = (SparrowhawkCodegenOptionalStruct) other; 170 | if (timestamp != o.timestamp) { 171 | return false; 172 | } 173 | if (!Objects.equals(getString(), o.getString())) { 174 | return false; 175 | } 176 | return true; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/StructureMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.*; 9 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.*; 10 | 11 | import java.nio.ByteBuffer; 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | import java.util.function.Supplier; 16 | 17 | @SuppressWarnings("unchecked") 18 | public final class StructureMap implements SparrowhawkObject { 19 | private static final long REQUIRED_LIST_FIELDSET_0 = KConstants.listField(0b11); 20 | private static final ByteBuffer[] EMPTY_KEYS = new ByteBuffer[0]; 21 | private static final Object[] EMPTY_VALUES = new Object[0]; 22 | 23 | private ByteBuffer[] keys; 24 | private Object[] values; 25 | private final Supplier factory; 26 | 27 | public StructureMap(Supplier factory) { 28 | this.factory = factory; 29 | } 30 | 31 | public Map toMap() { 32 | int sz = keys.length; 33 | T[] values = (T[]) this.values; 34 | Map m = new HashMap<>(sz / 3 * 4); 35 | for (int i = 0; i < sz; i++) { 36 | m.put(string(keys[i]), values[i]); 37 | } 38 | return m; 39 | } 40 | 41 | private static String string(ByteBuffer b) { 42 | return new String(b.array(), b.arrayOffset() + b.position(), b.remaining(), StandardCharsets.UTF_8); 43 | } 44 | 45 | public void fromMap(Map map) { 46 | int len = map.size(); 47 | if (len == 0) { 48 | keys = EMPTY_KEYS; 49 | values = EMPTY_VALUES; 50 | $size = 0; 51 | return; 52 | } 53 | 54 | ByteBuffer[] keys = new ByteBuffer[len]; 55 | this.keys = keys; 56 | T[] values = (T[]) new SparrowhawkObject[len]; 57 | this.values = values; 58 | int i = 0; 59 | int size = 1 + (2 * uintSize(encodeLenPrefixedListLength(len))); 60 | for (Map.Entry entry : map.entrySet()) { 61 | byte[] key = entry.getKey().getBytes(StandardCharsets.UTF_8); 62 | keys[i] = ByteBuffer.wrap(key); 63 | T value = entry.getValue(); 64 | values[i++] = value; 65 | size += byteListLengthEncodedSize(key.length) + byteListLengthEncodedSize(value.size()); 66 | } 67 | this.$size = size; 68 | } 69 | 70 | @Override 71 | public void decodeFrom(SparrowhawkDeserializer d) { 72 | int size = (int) decodeElementCount(d.varUI()); 73 | $size = size; 74 | if (size > 0) { 75 | long fieldset = d.varUL(); 76 | if ((fieldset & 3) != KConstants.T_LIST) { 77 | throw new RuntimeException("bad field type: " + KConstants.fieldType(fieldset)); 78 | } 79 | if ((fieldset & REQUIRED_LIST_FIELDSET_0) != REQUIRED_LIST_FIELDSET_0) { 80 | throw new RuntimeException("missing required fields"); 81 | } 82 | int nkeys = (int) decodeElementCount(d.varUI()); 83 | keys = readKeys(d, nkeys); 84 | int nvalues = (int) decodeElementCount(d.varUI()); 85 | if (nkeys != nvalues) { 86 | throw new RuntimeException("mismatch in key and value lengths"); 87 | } 88 | values = readValues(d, nvalues); 89 | } else { 90 | keys = EMPTY_KEYS; 91 | values = (T[]) EMPTY_VALUES; 92 | } 93 | } 94 | 95 | private static ByteBuffer[] readKeys(SparrowhawkDeserializer d, int n) { 96 | ByteBuffer[] bs = new ByteBuffer[n]; 97 | for (int i = 0; i < bs.length; i++) { 98 | bs[i] = d.bytes(); 99 | } 100 | return bs; 101 | } 102 | 103 | private T[] readValues(SparrowhawkDeserializer d, int n) { 104 | T[] values = (T[]) new SparrowhawkObject[n]; 105 | for (int i = 0; i < values.length; i++) { 106 | T obj = factory.get(); 107 | obj.decodeFrom(d); 108 | values[i] = obj; 109 | } 110 | return values; 111 | } 112 | 113 | @Override 114 | public void encodeTo(SparrowhawkSerializer s) { 115 | int size = size(); 116 | if (size > 0) { 117 | s.writeVarUL(encodeByteListLength(size)); 118 | s.writeVarUL(REQUIRED_LIST_FIELDSET_0); 119 | long dl = encodeLenPrefixedListLength(keys.length); 120 | s.writeVarUL(dl); 121 | for (int i = 0; i < keys.length; i++) { 122 | s.writeBytes(keys[i]); 123 | } 124 | s.writeVarUL(dl); 125 | T[] values = (T[]) this.values; 126 | for (int i = 0; i < values.length; i++) { 127 | values[i].encodeTo(s); 128 | } 129 | } else { 130 | s.writeRawByte(EMPTY_LIST_SIZE_VARINT); 131 | } 132 | } 133 | 134 | private transient int $size; 135 | 136 | @Override 137 | public int size() { 138 | int size = $size; 139 | if (size >= 0) { 140 | return size; 141 | } 142 | 143 | if (keys.length != values.length) { 144 | return invalidMap(); 145 | } 146 | 147 | if (keys.length > 0) { 148 | size = 1; // required list field 0 149 | size += (2 * ulongSize(encodeLenPrefixedListLength(keys.length))); 150 | for (int i = 0; i < keys.length; i++) { 151 | size += byteListLengthEncodedSize(keys[i].remaining()); 152 | } 153 | T[] values = (T[]) this.values; 154 | for (int i = 0; i < values.length; i++) { 155 | size += byteListLengthEncodedSize(values[i].size()); 156 | } 157 | } else { 158 | size = 0; 159 | } 160 | 161 | this.$size = size; 162 | return size; 163 | } 164 | 165 | private static int invalidMap() { 166 | throw new RuntimeException("invalid map"); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/StringMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.*; 9 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.*; 10 | 11 | import java.nio.ByteBuffer; 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.HashMap; 14 | import java.util.Iterator; 15 | import java.util.Map; 16 | 17 | public final class StringMap implements SparrowhawkObject { 18 | private static final long REQUIRED_LIST_FIELDSET_0 = KConstants.listField(0b11); 19 | private static final ByteBuffer[] EMPTY = new ByteBuffer[0]; 20 | 21 | private transient int $size = -1; 22 | private ByteBuffer[] keys; 23 | private ByteBuffer[] values; 24 | 25 | public void fromMap(Map m) { 26 | int len = m.size(); 27 | if (len == 0) { 28 | keys = values = EMPTY; 29 | $size = 0; 30 | return; 31 | } 32 | 33 | ByteBuffer[] keys = new ByteBuffer[len]; 34 | ByteBuffer[] values = new ByteBuffer[len]; 35 | this.keys = keys; 36 | this.values = values; 37 | // fieldset + 2List lengths 38 | int size = 1 + (2 * uintSize(encodeLenPrefixedListLength(len))); 39 | size += encodeEntries(m, keys, values); 40 | $size = size; 41 | } 42 | 43 | private static int encodeEntries(Map m, ByteBuffer[] keys, ByteBuffer[] values) { 44 | if (keys.length != values.length) throw new IllegalArgumentException(); 45 | if (keys.length == 0) return 0; 46 | Iterator> iter = m.entrySet().iterator(); 47 | int s = 0; 48 | for (int i = 0; i < keys.length; i++) { 49 | Map.Entry e = iter.next(); 50 | s += encodeEntry(e, keys, values, i); 51 | } 52 | return s; 53 | } 54 | 55 | private static int encodeEntry(Map.Entry e, ByteBuffer[] keys, ByteBuffer[] values, int i) { 56 | byte[] key = k(e); 57 | byte[] value = v(e); 58 | keys[i] = ByteBuffer.wrap(key); 59 | values[i] = ByteBuffer.wrap(value); 60 | return l(key, value); 61 | } 62 | 63 | private static byte[] k(Map.Entry e) { 64 | return e.getKey().getBytes(StandardCharsets.UTF_8); 65 | } 66 | 67 | private static byte[] v(Map.Entry e) { 68 | return e.getValue().getBytes(StandardCharsets.UTF_8); 69 | } 70 | 71 | private static int l(byte[] key, byte[] value) { 72 | return byteListLengthEncodedSize(key.length) + byteListLengthEncodedSize(value.length); 73 | } 74 | 75 | public Map toMap() { 76 | int sz = keys.length; 77 | Map m = new HashMap<>(sz / 3 * 4); 78 | for (int i = 0; i < sz; i++) { 79 | m.put(string(keys[i]), string(values[i])); 80 | } 81 | return m; 82 | } 83 | 84 | private static String string(ByteBuffer b) { 85 | return new String(b.array(), b.arrayOffset() + b.position(), b.remaining(), StandardCharsets.UTF_8); 86 | } 87 | 88 | @Override 89 | public void decodeFrom(SparrowhawkDeserializer d) { 90 | $size = (int) decodeElementCount(d.varUI()); 91 | if ($size > 0) { 92 | long fieldset = d.varUL(); 93 | if ((fieldset & 3) != KConstants.T_LIST) { 94 | throw new RuntimeException("bad field type: " + KConstants.fieldType(fieldset)); 95 | } 96 | if ((fieldset & REQUIRED_LIST_FIELDSET_0) != REQUIRED_LIST_FIELDSET_0) { 97 | throw new RuntimeException("missing required fields"); 98 | } 99 | int nkeys = (int) decodeElementCount(d.varUI()); 100 | keys = readBytes(d, nkeys); 101 | int nvalues = (int) decodeElementCount(d.varUI()); 102 | if (nkeys != nvalues) { 103 | throw new RuntimeException("mismatch in key and value lengths"); 104 | } 105 | values = readBytes(d, nvalues); 106 | } else { 107 | keys = values = EMPTY; 108 | } 109 | } 110 | 111 | private ByteBuffer[] readBytes(SparrowhawkDeserializer d, int n) { 112 | ByteBuffer[] bs = new ByteBuffer[n]; 113 | for (int i = 0; i < bs.length; i++) { 114 | bs[i] = d.bytes(); 115 | } 116 | return bs; 117 | } 118 | 119 | @Override 120 | public void encodeTo(SparrowhawkSerializer s) { 121 | int size = size(); 122 | s.writeVarUL(encodeByteListLength(size)); 123 | if (size > 0) { 124 | s.writeVarUL(REQUIRED_LIST_FIELDSET_0); 125 | long dl = encodeLenPrefixedListLength(keys.length); 126 | s.writeVarUL(dl); 127 | for (int i = 0; i < keys.length; i++) { 128 | s.writeBytes(keys[i]); 129 | } 130 | s.writeVarUL(dl); 131 | for (int i = 0; i < values.length; i++) { 132 | s.writeBytes(values[i]); 133 | } 134 | } 135 | } 136 | 137 | @Override 138 | public int size() { 139 | int size = this.$size; 140 | if (size >= 0) { 141 | return size; 142 | } 143 | 144 | if (keys.length != values.length) { 145 | return invalidMap(); 146 | } 147 | 148 | if (keys.length > 0) { 149 | size = 1; // required list field 0 150 | size += (2 * ulongSize(encodeLenPrefixedListLength(keys.length))); 151 | for (int i = 0; i < keys.length; i++) { 152 | size += byteListLengthEncodedSize(keys[i].remaining()); 153 | } 154 | for (int i = 0; i < values.length; i++) { 155 | size += byteListLengthEncodedSize(values[i].remaining()); 156 | } 157 | } else { 158 | size = 0; // 0 size written as 1 byte 159 | } 160 | 161 | this.$size = size; 162 | return size; 163 | } 164 | 165 | private static int invalidMap() { 166 | throw new RuntimeException("invalid map"); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/DirectedSparrowhawkCodegen.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Comparator; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import software.amazon.smithy.codegen.core.SymbolProvider; 13 | import software.amazon.smithy.codegen.core.directed.*; 14 | import software.amazon.smithy.model.Model; 15 | import software.amazon.smithy.model.knowledge.NullableIndex; 16 | import software.amazon.smithy.model.shapes.*; 17 | import software.amazon.smithy.model.transform.ModelTransformer; 18 | import software.amazon.smithy.protocol.traits.IdxTrait; 19 | 20 | public final class DirectedSparrowhawkCodegen implements 21 | DirectedCodegen { 22 | @Override 23 | public SymbolProvider createSymbolProvider(CreateSymbolProviderDirective directive) { 24 | var model = directive.model(); 25 | var service = model.expectShape(directive.settings().getService(), ServiceShape.class); 26 | return new SparrowhawkSymbolVisitor(model, service, directive.settings()); 27 | } 28 | 29 | @Override 30 | public GenerationContext createContext( 31 | CreateContextDirective directive 32 | ) { 33 | return GenerationContext.builder() 34 | .model(preprocess(directive.model())) 35 | .fileManifest(directive.fileManifest()) 36 | .integrations(directive.integrations()) 37 | .symbolProvider(directive.symbolProvider()) 38 | .settings(directive.settings()) 39 | .writerDelegator( 40 | new JavaDelegator(directive.fileManifest(), directive.symbolProvider(), directive.settings()) 41 | ) 42 | .build(); 43 | } 44 | 45 | @Override 46 | public void generateService(GenerateServiceDirective directive) { 47 | 48 | } 49 | 50 | @Override 51 | public void generateOperation(GenerateOperationDirective directive) { 52 | 53 | } 54 | 55 | @Override 56 | public void generateStructure(GenerateStructureDirective directive) { 57 | generate(directive); 58 | } 59 | 60 | private static void generate(ShapeDirective directive) { 61 | 62 | directive.context().writerDelegator().useShapeWriter(directive.shape(), writer -> { 63 | new StructureGenerator( 64 | directive.shape(), 65 | directive.model(), 66 | directive.context().symbolProvider(), 67 | writer, 68 | directive.settings() 69 | ).run(); 70 | }); 71 | } 72 | 73 | @Override 74 | public void generateError(GenerateErrorDirective directive) { 75 | generate(directive); 76 | } 77 | 78 | @Override 79 | public void generateUnion(GenerateUnionDirective directive) { 80 | generate(directive); 81 | } 82 | 83 | @Override 84 | public void generateEnumShape(GenerateEnumDirective directive) { 85 | 86 | } 87 | 88 | @Override 89 | public void generateIntEnumShape(GenerateIntEnumDirective directive) { 90 | 91 | } 92 | 93 | private static Model preprocess(Model model) { 94 | var newShapes = new ArrayList(); 95 | for (StructureShape shape : model.getStructureShapes()) { 96 | transformMembers(model, shape, newShapes); 97 | } 98 | for (UnionShape shape : model.getUnionShapes()) { 99 | transformMembers(model, shape, newShapes); 100 | } 101 | var transform = ModelTransformer.create(); 102 | return transform.replaceShapes(model, newShapes); 103 | } 104 | 105 | private static void transformMembers(Model model, Shape shape, List newShapes) { 106 | NullableIndex idx = NullableIndex.of(model); 107 | List members = new ArrayList<>(shape.members()); 108 | if (members.isEmpty() || members.stream().noneMatch(m -> m.hasTrait(IdxTrait.class))) { 109 | return; 110 | } 111 | members.sort( 112 | Comparator.comparingInt( 113 | m -> m.getTrait(IdxTrait.class) 114 | .map(IdxTrait::getValue) 115 | .orElse(0) 116 | ) 117 | ); 118 | 119 | var membersByType = new HashMap>(); 120 | 121 | for (MemberShape ms : members) { 122 | FieldType type; 123 | switch (model.expectShape((ms.getTarget())).getType()) { 124 | case BLOB, STRING, DOCUMENT, BIG_DECIMAL, BIG_INTEGER, ENUM, LIST, SET, MAP, STRUCTURE, UNION -> 125 | type = FieldType.LIST; 126 | case BOOLEAN, BYTE, SHORT, INTEGER, LONG, INT_ENUM -> type = FieldType.VARINT; 127 | case TIMESTAMP, DOUBLE -> type = FieldType.EIGHT_BYTE; 128 | case FLOAT -> type = FieldType.FOUR_BYTE; 129 | default -> throw new IllegalArgumentException(); 130 | } 131 | membersByType.computeIfAbsent(type, $ -> new ArrayList<>()).add(ms); 132 | } 133 | for (var e : membersByType.entrySet()) { 134 | List value = e.getValue(); 135 | for (int i = 0; i < value.size(); i++) { 136 | var ms = value.get(i); 137 | newShapes.add( 138 | ms.toBuilder() 139 | .addTrait( 140 | new SparrowhawkFieldTrait( 141 | e.getKey(), 142 | i / 61, 143 | i % 61 + 1, 144 | !idx.isMemberNullable(ms, NullableIndex.CheckMode.SERVER) 145 | ) 146 | ) 147 | .build() 148 | ); 149 | } 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/SparseStructureMap.java: -------------------------------------------------------------------------------- 1 | package software.amazon.smithy.java.sparrowhawk; 2 | 3 | import static software.amazon.smithy.java.sparrowhawk.KConstants.decodeElementCount; 4 | import static software.amazon.smithy.java.sparrowhawk.KConstants.encodeByteListLength; 5 | import static software.amazon.smithy.java.sparrowhawk.KConstants.encodeLenPrefixedListLength; 6 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.EMPTY_LIST_SIZE_VARINT; 7 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.byteListLengthEncodedSize; 8 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.uintSize; 9 | import static software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer.ulongSize; 10 | 11 | import java.nio.ByteBuffer; 12 | import java.nio.charset.StandardCharsets; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | import java.util.function.Supplier; 16 | 17 | @SuppressWarnings("rawtypes,unchecked") 18 | public final class SparseStructureMap implements SparrowhawkObject { 19 | private static final long REQUIRED_LIST_FIELDSET_0 = KConstants.listField(0b11); 20 | private static final ByteBuffer[] EMPTY_KEYS = new ByteBuffer[0]; 21 | private static final OptionalObject[] EMPTY_VALUES = new OptionalObject[0]; 22 | 23 | private ByteBuffer[] keys; 24 | private OptionalObject[] values; 25 | private final Supplier factory; 26 | 27 | public SparseStructureMap(Supplier factory) { 28 | this.factory = factory; 29 | } 30 | 31 | public Map toMap() { 32 | int sz = keys.length; 33 | OptionalObject[] values = this.values; 34 | Map m = new HashMap<>(sz / 3 * 4); 35 | for (int i = 0; i < sz; i++) { 36 | m.put(string(keys[i]), values[i].getItem()); 37 | } 38 | return m; 39 | } 40 | 41 | 42 | private static String string(ByteBuffer b) { 43 | return new String(b.array(), b.arrayOffset() + b.position(), b.remaining(), StandardCharsets.UTF_8); 44 | } 45 | 46 | public void fromMap(Map map) { 47 | int len = map.size(); 48 | if (len == 0) { 49 | keys = EMPTY_KEYS; 50 | values = EMPTY_VALUES; 51 | $size = 0; 52 | return; 53 | } 54 | 55 | ByteBuffer[] keys = new ByteBuffer[len]; 56 | this.keys = keys; 57 | OptionalObject[] values = (OptionalObject[]) new OptionalObject[len]; 58 | this.values = values; 59 | int i = 0; 60 | int size = 1 + (2 * uintSize(encodeLenPrefixedListLength(len))); 61 | for (Map.Entry entry : map.entrySet()) { 62 | byte[] key = entry.getKey().getBytes(StandardCharsets.UTF_8); 63 | keys[i] = ByteBuffer.wrap(key); 64 | T value = entry.getValue(); 65 | size += byteListLengthEncodedSize(key.length); 66 | if (value != null) { 67 | OptionalObject obj = new OptionalObject<>(factory); 68 | obj.setItem(value); 69 | values[i] = obj; 70 | size += byteListLengthEncodedSize(obj.size()); 71 | } else { 72 | size += 1; 73 | } 74 | i++; 75 | } 76 | this.$size = size; 77 | } 78 | 79 | @Override 80 | public void decodeFrom(SparrowhawkDeserializer d) { 81 | int size = (int) decodeElementCount(d.varUI()); 82 | $size = size; 83 | if (size > 0) { 84 | long fieldset = d.varUL(); 85 | if ((fieldset & 3) != KConstants.T_LIST) { 86 | throw new RuntimeException("bad field type: " + KConstants.fieldType(fieldset)); 87 | } 88 | if ((fieldset & REQUIRED_LIST_FIELDSET_0) != REQUIRED_LIST_FIELDSET_0) { 89 | throw new RuntimeException("missing required fields"); 90 | } 91 | int nkeys = (int) decodeElementCount(d.varUI()); 92 | keys = readKeys(d, nkeys); 93 | int nvalues = (int) decodeElementCount(d.varUI()); 94 | if (nkeys != nvalues) { 95 | throw new RuntimeException("mismatch in key and value lengths"); 96 | } 97 | values = readValues(d, nvalues); 98 | } else { 99 | keys = EMPTY_KEYS; 100 | values = EMPTY_VALUES; 101 | } 102 | } 103 | 104 | private static ByteBuffer[] readKeys(SparrowhawkDeserializer d, int n) { 105 | ByteBuffer[] bs = new ByteBuffer[n]; 106 | for (int i = 0; i < bs.length; i++) { 107 | bs[i] = d.bytes(); 108 | } 109 | return bs; 110 | } 111 | 112 | private OptionalObject[] readValues(SparrowhawkDeserializer d, int n) { 113 | OptionalObject[] values = new OptionalObject[n]; 114 | for (int i = 0; i < values.length; i++) { 115 | // it would be nice to remove the object allocation and inline the decoding routine here 116 | OptionalObject o = new OptionalObject<>(factory); 117 | o.decodeFrom(d); 118 | values[i] = o; 119 | } 120 | return values; 121 | } 122 | 123 | @Override 124 | public void encodeTo(SparrowhawkSerializer s) { 125 | int size = size(); 126 | if (size > 0) { 127 | s.writeVarUL(encodeByteListLength(size)); 128 | s.writeVarUL(REQUIRED_LIST_FIELDSET_0); 129 | long dl = encodeLenPrefixedListLength(keys.length); 130 | s.writeVarUL(dl); 131 | for (int i = 0; i < keys.length; i++) { 132 | s.writeBytes(keys[i]); 133 | } 134 | s.writeVarUL(dl); 135 | OptionalObject[] values = this.values; 136 | for (int i = 0; i < values.length; i++) { 137 | if (values[i] == null) { 138 | s.writeRawByte((byte) 1); // list length of zero, indicating an empty OptionalObject 139 | } else { 140 | values[i].encodeTo(s); 141 | } 142 | } 143 | } else { 144 | s.writeRawByte(EMPTY_LIST_SIZE_VARINT); 145 | } 146 | } 147 | 148 | private transient int $size; 149 | 150 | @Override 151 | public int size() { 152 | int size = $size; 153 | if (size >= 0) { 154 | return size; 155 | } 156 | 157 | if (keys.length != values.length) { 158 | return invalidMap(); 159 | } 160 | 161 | if (keys.length > 0) { 162 | size = 1; // required list field 0 163 | size += (2 * ulongSize(encodeLenPrefixedListLength(keys.length))); 164 | for (int i = 0; i < keys.length; i++) { 165 | size += byteListLengthEncodedSize(keys[i].remaining()); 166 | } 167 | OptionalObject[] values = this.values; 168 | for (int i = 0; i < values.length; i++) { 169 | size += byteListLengthEncodedSize(values[i].size()); 170 | } 171 | } else { 172 | size = 0; 173 | } 174 | 175 | this.$size = size; 176 | return size; 177 | } 178 | 179 | private static int invalidMap() { 180 | throw new RuntimeException("invalid map"); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/CommonSymbols.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import java.nio.ByteBuffer; 9 | import software.amazon.smithy.codegen.core.Symbol; 10 | import software.amazon.smithy.codegen.core.SymbolReference; 11 | 12 | public final class CommonSymbols { 13 | private CommonSymbols() {} 14 | 15 | 16 | public enum UseOption implements SymbolReference.Option { 17 | STATIC 18 | } 19 | 20 | public static SymbolReference staticImp(String namespace, String name) { 21 | return Symbol.builder() 22 | .namespace(namespace, ".") 23 | .name(name) 24 | .build() 25 | .toReference(null, UseOption.STATIC); 26 | } 27 | 28 | public static SymbolReference imp(Class klass) { 29 | return Symbol.builder() 30 | .namespace(klass.getPackageName(), ".") 31 | .name(klass.getSimpleName()) 32 | .build() 33 | .toReference(null); 34 | } 35 | 36 | public static SymbolReference imp(String namespace, String name) { 37 | return Symbol.builder() 38 | .namespace(namespace, ".") 39 | .name(name) 40 | .build() 41 | .toReference(null); 42 | } 43 | 44 | public static final SymbolReference UTF_8 = staticImp("java.nio.charset.StandardCharsets", "UTF_8"); 45 | public static final SymbolReference asList = staticImp("java.util.Arrays", "asList"); 46 | public static final SymbolReference toMap = staticImp("java.util.stream.Collectors", "toMap"); 47 | public static final SymbolReference toList = staticImp("java.util.stream.Collectors", "toList"); 48 | public static final SymbolReference Entry = imp("java.util.Map", "Entry"); 49 | public static final SymbolReference SimpleEntry = imp("java.util.AbstractMap", "SimpleEntry"); 50 | public static final SymbolReference Object = imp("java.lang", "Object"); 51 | public static final SymbolReference Objects = imp("java.util", "Objects"); 52 | public static final SymbolReference missingField = staticImp( 53 | "software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer", 54 | "missingField" 55 | ); 56 | public static final SymbolReference intSize = staticImp( 57 | "software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer", 58 | "intSize" 59 | ); 60 | public static final SymbolReference longSize = staticImp( 61 | "software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer", 62 | "longSize" 63 | ); 64 | public static final SymbolReference uintSize = staticImp( 65 | "software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer", 66 | "uintSize" 67 | ); 68 | public static final SymbolReference ulongSize = staticImp( 69 | "software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer", 70 | "ulongSize" 71 | ); 72 | public static final SymbolReference byteListLengthEncodedSize = staticImp( 73 | "software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer", 74 | "byteListLengthEncodedSize" 75 | ); 76 | public static final SymbolReference lenPrefixedListLengthEncodedSize = staticImp( 77 | "software.amazon.smithy.java.sparrowhawk.SparrowhawkSerializer", 78 | "lenPrefixedListLengthEncodedSize" 79 | ); 80 | public static final SymbolReference encodeFourBListLength = staticImp( 81 | "software.amazon.smithy.java.sparrowhawk.KConstants", 82 | "encodeFourBListLength" 83 | ); 84 | public static final SymbolReference encodeEightBListLength = staticImp( 85 | "software.amazon.smithy.java.sparrowhawk.KConstants", 86 | "encodeEightBListLength" 87 | ); 88 | public static final SymbolReference encodeVarintListLength = staticImp( 89 | "software.amazon.smithy.java.sparrowhawk.KConstants", 90 | "encodeVarintListLength" 91 | ); 92 | public static final SymbolReference encodeByteListLength = staticImp( 93 | "software.amazon.smithy.java.sparrowhawk.KConstants", 94 | "encodeByteListLength" 95 | ); 96 | public static final SymbolReference encodeLenPrefixedListLength = staticImp( 97 | "software.amazon.smithy.java.sparrowhawk.KConstants", 98 | "encodeLenPrefixedListLength" 99 | ); 100 | public static final SymbolReference decodeElementCount = staticImp( 101 | "software.amazon.smithy.java.sparrowhawk.KConstants", 102 | "decodeElementCount" 103 | ); 104 | public static final SymbolReference decodeVarintListLengthChecked = staticImp( 105 | "software.amazon.smithy.java.sparrowhawk.KConstants", 106 | "decodeVarintListLengthChecked" 107 | ); 108 | public static final SymbolReference decodeLenPrefixedListLengthChecked = staticImp( 109 | "software.amazon.smithy.java.sparrowhawk.KConstants", 110 | "decodeLenPrefixedListLengthChecked" 111 | ); 112 | public static final SymbolReference decodeFourByteListLengthChecked = staticImp( 113 | "software.amazon.smithy.java.sparrowhawk.KConstants", 114 | "decodeFourByteListLengthChecked" 115 | ); 116 | public static final SymbolReference decodeEightByteListLengthChecked = staticImp( 117 | "software.amazon.smithy.java.sparrowhawk.KConstants", 118 | "decodeEightByteListLengthChecked" 119 | ); 120 | public static final SymbolReference T_LIST = staticImp( 121 | "software.amazon.smithy.java.sparrowhawk.KConstants", 122 | "T_LIST" 123 | ); 124 | public static final SymbolReference T_VARINT = staticImp( 125 | "software.amazon.smithy.java.sparrowhawk.KConstants", 126 | "T_VARINT" 127 | ); 128 | public static final SymbolReference T_FOUR = staticImp( 129 | "software.amazon.smithy.java.sparrowhawk.KConstants", 130 | "T_FOUR" 131 | ); 132 | public static final SymbolReference T_EIGHT = staticImp( 133 | "software.amazon.smithy.java.sparrowhawk.KConstants", 134 | "T_EIGHT" 135 | ); 136 | 137 | public static final SymbolReference SparrowhawkObject = imp( 138 | "software.amazon.smithy.java.sparrowhawk", 139 | "SparrowhawkObject" 140 | ); 141 | public static final SymbolReference SparrowhawkSerializer = imp( 142 | "software.amazon.smithy.java.sparrowhawk", 143 | "SparrowhawkSerializer" 144 | ); 145 | public static final SymbolReference SparrowhawkDeserializer = imp( 146 | "software.amazon.smithy.java.sparrowhawk", 147 | "SparrowhawkDeserializer" 148 | ); 149 | 150 | public static final SymbolReference FloatMap = imp("software.amazon.smithy.java.sparrowhawk", "FloatMap"); 151 | public static final SymbolReference DoubleMap = imp("software.amazon.smithy.java.sparrowhawk", "DoubleMap"); 152 | public static final SymbolReference BooleanMap = imp("software.amazon.smithy.java.sparrowhawk", "BooleanMap"); 153 | public static final SymbolReference ByteMap = imp("software.amazon.smithy.java.sparrowhawk", "ByteMap"); 154 | public static final SymbolReference ShortMap = imp("software.amazon.smithy.java.sparrowhawk", "ShortMap"); 155 | public static final SymbolReference IntegerMap = imp("software.amazon.smithy.java.sparrowhawk", "IntegerMap"); 156 | public static final SymbolReference LongMap = imp("software.amazon.smithy.java.sparrowhawk", "LongMap"); 157 | public static final SymbolReference StringList = imp("software.amazon.smithy.java.sparrowhawk", "StringList"); 158 | public static final SymbolReference SparseStringList = imp( 159 | "software.amazon.smithy.java.sparrowhawk", 160 | "SparseStringList" 161 | ); 162 | public static final SymbolReference StringMap = imp("software.amazon.smithy.java.sparrowhawk", "StringMap"); 163 | public static final SymbolReference StructureMap = imp("software.amazon.smithy.java.sparrowhawk", "StructureMap"); 164 | 165 | public static final SymbolReference FLOW_PUBLISHER = Symbol.builder() 166 | .namespace("java.util.concurrent.Flow", ".") 167 | .name("Publisher") 168 | .build() 169 | .toReference(null); 170 | public static final SymbolReference BYTE_BUFFER = imp(ByteBuffer.class); 171 | } 172 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/SparrowhawkDeserializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.*; 9 | 10 | import java.nio.ByteBuffer; 11 | import java.nio.charset.StandardCharsets; 12 | import java.time.Instant; 13 | import java.util.Arrays; 14 | import java.util.Date; 15 | import java.util.List; 16 | 17 | public final class SparrowhawkDeserializer { 18 | private final byte[] b; 19 | private final int len; 20 | private int pos; 21 | 22 | public SparrowhawkDeserializer(byte[] b) { 23 | this.b = b; 24 | this.len = b.length; 25 | } 26 | 27 | public SparrowhawkDeserializer(byte[] b, int off, int len) { 28 | this.b = b; 29 | this.pos = off; 30 | this.len = len; 31 | } 32 | 33 | public SparrowhawkDeserializer(ByteBuffer b) { 34 | if (b.hasArray()) { 35 | this.b = b.array(); 36 | this.pos = b.position() + b.arrayOffset(); 37 | this.len = b.remaining(); 38 | } else { 39 | byte[] bytes = bytes(b); 40 | this.b = bytes; 41 | this.len = bytes.length; 42 | } 43 | } 44 | 45 | private static byte[] bytes(ByteBuffer b) { 46 | byte[] bytes = new byte[b.remaining()]; 47 | b.get(bytes); 48 | return bytes; 49 | } 50 | 51 | public int pos() { 52 | return pos; 53 | } 54 | 55 | public void done() { 56 | if (pos != len) { 57 | throw new RuntimeException("still has " + (len - pos) + "bytes"); 58 | } 59 | } 60 | 61 | public String string() { 62 | int len = (int) varUL(); 63 | if (!KConstants.isByteListLength(len)) { 64 | throw new RuntimeException("not bytes: " + listType(len)); 65 | } 66 | int decodedLen = decodeByteListLength(len); 67 | String s = new String(b, pos, decodedLen, StandardCharsets.UTF_8); 68 | pos += decodedLen; 69 | return s; 70 | } 71 | 72 | public ByteBuffer bytes() { 73 | int len = (int) varUL(); 74 | if (!KConstants.isByteListLength(len)) { 75 | throw new RuntimeException("not bytes: " + listType(len)); 76 | } 77 | int decodedLen = decodeByteListLength(len); 78 | ByteBuffer bb = ByteBuffer.wrap(b, pos, decodedLen).slice(); 79 | pos += decodedLen; 80 | return bb; 81 | } 82 | 83 | public ByteBuffer object() { 84 | int start = pos; 85 | int len = (int) varUL(); 86 | int prefix = pos - start; 87 | if (!KConstants.isByteListLength(len)) { 88 | throw new RuntimeException("not bytes: " + listType(len)); 89 | } 90 | int decodedLen = decodeByteListLength(len); 91 | ByteBuffer bb = ByteBuffer.wrap(b, start, decodedLen + prefix).slice(); 92 | pos += decodedLen; 93 | return bb; 94 | } 95 | 96 | public float f4() { 97 | float f = Float.intBitsToFloat(read4(b, pos)); 98 | pos += 4; 99 | return f; 100 | } 101 | 102 | public double d8() { 103 | double d = Double.longBitsToDouble(read8(b, pos)); 104 | pos += 8; 105 | return d; 106 | } 107 | 108 | public Date date() { 109 | return new Date(Math.round(d8() * 1000)); 110 | } 111 | 112 | public Instant instant() { 113 | return Instant.ofEpochMilli(Math.round(d8() * 1000)); 114 | } 115 | 116 | public boolean bool() { 117 | return varUL() != 0; 118 | } 119 | 120 | public byte varB() { 121 | return (byte) varI(); 122 | } 123 | 124 | public short varS() { 125 | return (short) varI(); 126 | } 127 | 128 | public int varI() { 129 | return zigzag4(varUI()); 130 | } 131 | 132 | private static int zigzag4(int i) { 133 | return (i >>> 1) ^ -(i & 1); 134 | } 135 | 136 | public int varUI() { 137 | return (int) varUL(); 138 | } 139 | 140 | public long varL() { 141 | return zigzag8(varUL()); 142 | } 143 | 144 | private static long zigzag8(long i) { 145 | return (i >>> 1) ^ -(i & 1L); 146 | } 147 | 148 | public long varUL() { 149 | int f = b[pos++] & 0xFF; 150 | if ((f & 1) == 1) { 151 | return (f >> 1); 152 | } 153 | 154 | int len = 1 + Integer.numberOfTrailingZeros(f | (1 << 8)); 155 | if (len == 9) { 156 | long v = read8(b, pos); 157 | pos += 8; 158 | return v; 159 | } 160 | 161 | long acc = f >> len; 162 | for (int i = 1; i < len; i++) { 163 | long update = ((long) (b[pos++] & 0xFF)) << ((8 * i) - len); 164 | acc |= update; 165 | } 166 | return acc; 167 | } 168 | 169 | public List decodeByteList() { 170 | int sz = decodeVarintListLengthChecked(varUL()); 171 | Byte[] bytes = new Byte[sz]; 172 | for (int i = 0; i < sz; i++) { 173 | bytes[i] = varB(); 174 | } 175 | return Arrays.asList(bytes); 176 | } 177 | 178 | public List decodeShortList() { 179 | int sz = decodeVarintListLengthChecked(varUL()); 180 | Short[] shorts = new Short[sz]; 181 | for (int i = 0; i < sz; i++) { 182 | shorts[i] = varS(); 183 | } 184 | return Arrays.asList(shorts); 185 | } 186 | 187 | public List decodeIntegerList() { 188 | int sz = decodeVarintListLengthChecked(varUL()); 189 | Integer[] integers = new Integer[sz]; 190 | for (int i = 0; i < sz; i++) { 191 | integers[i] = varI(); 192 | } 193 | return Arrays.asList(integers); 194 | } 195 | 196 | public List decodeLongList() { 197 | int sz = decodeVarintListLengthChecked(varUL()); 198 | Long[] longs = new Long[sz]; 199 | for (int i = 0; i < sz; i++) { 200 | longs[i] = varL(); 201 | } 202 | return Arrays.asList(longs); 203 | } 204 | 205 | public List decodeDoubleList() { 206 | int sz = decodeEightByteListLengthChecked(varUL()); 207 | Double[] doubles = new Double[sz]; 208 | for (int i = 0; i < sz; i++) { 209 | doubles[i] = d8(); 210 | } 211 | return Arrays.asList(doubles); 212 | } 213 | 214 | public List decodeFloatList() { 215 | int sz = decodeFourByteListLengthChecked(varUI()); 216 | Float[] floats = new Float[sz]; 217 | for (int i = 0; i < sz; i++) { 218 | floats[i] = f4(); 219 | } 220 | return Arrays.asList(floats); 221 | } 222 | 223 | public List decodeBooleanList() { 224 | int sz = decodeVarintListLengthChecked(varUL()); 225 | Boolean[] booleans = new Boolean[sz]; 226 | for (int i = 0; i < sz; i++) { 227 | booleans[i] = bool(); 228 | } 229 | return Arrays.asList(booleans); 230 | } 231 | 232 | private static int read4(byte[] b, int off) { 233 | return (b[off] & 0xFF) 234 | | (b[off + 1] & 0xFF) << 8 235 | | (b[off + 2] & 0xFF) << 16 236 | | (b[off + 3] & 0xFF) << 24; 237 | } 238 | 239 | static long read8(byte[] b, int off) { 240 | return (long) (b[off] & 0xFF) 241 | | (long) (b[off + 1] & 0xFF) << 8 242 | | (long) (b[off + 2] & 0xFF) << 16 243 | | (long) (b[off + 3] & 0xFF) << 24 244 | | (long) (b[off + 4] & 0xFF) << 32 245 | | (long) (b[off + 5] & 0xFF) << 40 246 | | (long) (b[off + 6] & 0xFF) << 48 247 | | (long) (b[off + 7] & 0xFF) << 56; 248 | } 249 | 250 | public static void checkFields(long fieldSet, long expected, String type) { 251 | if ((fieldSet & expected) != expected) { 252 | throw new RuntimeException("missing required " + type + " fields"); 253 | } 254 | } 255 | 256 | public static void checkUnset(long fieldSet, String type, int index) { 257 | if (fieldSet != 0) { 258 | throw new RuntimeException(type + " " + index + " is already set"); 259 | } 260 | } 261 | 262 | public static void checkFieldsExact(long fieldSet, long expected, String type) { 263 | if (fieldSet != expected) { 264 | throw new RuntimeException( 265 | "incorrect fieldset for " + type + ": expected " + expected + ", got " + fieldSet 266 | ); 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Use the maximum available, or set MAX_FD != -1 to use that value. 89 | MAX_FD=maximum 90 | 91 | warn () { 92 | echo "$*" 93 | } >&2 94 | 95 | die () { 96 | echo 97 | echo "$*" 98 | echo 99 | exit 1 100 | } >&2 101 | 102 | # OS specific support (must be 'true' or 'false'). 103 | cygwin=false 104 | msys=false 105 | darwin=false 106 | nonstop=false 107 | case "$( uname )" in #( 108 | CYGWIN* ) cygwin=true ;; #( 109 | Darwin* ) darwin=true ;; #( 110 | MSYS* | MINGW* ) msys=true ;; #( 111 | NONSTOP* ) nonstop=true ;; 112 | esac 113 | 114 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 115 | 116 | 117 | # Determine the Java command to use to start the JVM. 118 | if [ -n "$JAVA_HOME" ] ; then 119 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 120 | # IBM's JDK on AIX uses strange locations for the executables 121 | JAVACMD=$JAVA_HOME/jre/sh/java 122 | else 123 | JAVACMD=$JAVA_HOME/bin/java 124 | fi 125 | if [ ! -x "$JAVACMD" ] ; then 126 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 127 | 128 | Please set the JAVA_HOME variable in your environment to match the 129 | location of your Java installation." 130 | fi 131 | else 132 | JAVACMD=java 133 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 134 | 135 | Please set the JAVA_HOME variable in your environment to match the 136 | location of your Java installation." 137 | fi 138 | 139 | # Increase the maximum file descriptors if we can. 140 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 141 | case $MAX_FD in #( 142 | max*) 143 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 144 | # shellcheck disable=SC3045 145 | MAX_FD=$( ulimit -H -n ) || 146 | warn "Could not query maximum file descriptor limit" 147 | esac 148 | case $MAX_FD in #( 149 | '' | soft) :;; #( 150 | *) 151 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 152 | # shellcheck disable=SC3045 153 | ulimit -n "$MAX_FD" || 154 | warn "Could not set maximum file descriptor limit to $MAX_FD" 155 | esac 156 | fi 157 | 158 | # Collect all arguments for the java command, stacking in reverse order: 159 | # * args from the command line 160 | # * the main class name 161 | # * -classpath 162 | # * -D...appname settings 163 | # * --module-path (only if needed) 164 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 165 | 166 | # For Cygwin or MSYS, switch paths to Windows format before running java 167 | if "$cygwin" || "$msys" ; then 168 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 169 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 170 | 171 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 172 | 173 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 174 | for arg do 175 | if 176 | case $arg in #( 177 | -*) false ;; # don't mess with options #( 178 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 179 | [ -e "$t" ] ;; #( 180 | *) false ;; 181 | esac 182 | then 183 | arg=$( cygpath --path --ignore --mixed "$arg" ) 184 | fi 185 | # Roll the args list around exactly as many times as the number of 186 | # args, so each arg winds up back in the position where it started, but 187 | # possibly modified. 188 | # 189 | # NB: a `for` loop captures its iteration list before it begins, so 190 | # changing the positional parameters here affects neither the number of 191 | # iterations, nor the values presented in `arg`. 192 | shift # remove old arg 193 | set -- "$@" "$arg" # push replacement arg 194 | done 195 | fi 196 | 197 | 198 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 199 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 200 | 201 | # Collect all arguments for the java command; 202 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 203 | # shell script including quotes and variable substitutions, so put them in 204 | # double quotes to make sure that they get re-expanded; and 205 | # * put everything else in single quotes, so that it's not re-expanded. 206 | 207 | set -- \ 208 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 209 | -classpath "$CLASSPATH" \ 210 | org.gradle.wrapper.GradleWrapperMain \ 211 | "$@" 212 | 213 | # Stop when "xargs" is not available. 214 | if ! command -v xargs >/dev/null 2>&1 215 | then 216 | die "xargs is not available" 217 | fi 218 | 219 | # Use "xargs" to parse quoted args. 220 | # 221 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 222 | # 223 | # In Bash we could simply go: 224 | # 225 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 226 | # set -- "${ARGS[@]}" "$@" 227 | # 228 | # but POSIX shell has neither arrays nor command substitution, so instead we 229 | # post-process each arg (as a line of input to sed) to backslash-escape any 230 | # character that might be a shell metacharacter, then use eval to reverse 231 | # that process (while maintaining the separation between arguments), and wrap 232 | # the whole thing up as a single "set" statement. 233 | # 234 | # This will of course break if any of these variables contains a newline or 235 | # an unmatched quote. 236 | # 237 | 238 | eval "set -- $( 239 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 240 | xargs -n1 | 241 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 242 | tr '\n' ' ' 243 | )" '"$@"' 244 | 245 | exec "$JAVACMD" "$@" 246 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | -------------------------------------------------------------------------------- /sparrowhawk-types/src/main/java/software/amazon/smithy/java/sparrowhawk/SparrowhawkSerializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.java.sparrowhawk; 7 | 8 | import static software.amazon.smithy.java.sparrowhawk.KConstants.*; 9 | 10 | import java.nio.ByteBuffer; 11 | import java.nio.charset.StandardCharsets; 12 | import java.time.Instant; 13 | import java.util.Date; 14 | import java.util.List; 15 | 16 | public final class SparrowhawkSerializer { 17 | public static final byte EMPTY_LIST_SIZE_VARINT = 1; 18 | private static final byte BOOL_FALSE = 1, BOOL_TRUE = 3; 19 | 20 | private int position; 21 | private final byte[] payload; 22 | 23 | public SparrowhawkSerializer(int len) { 24 | this.payload = new byte[byteListLengthEncodedSize(len)]; 25 | } 26 | 27 | public SparrowhawkSerializer(byte[] payload) { 28 | this.payload = payload; 29 | } 30 | 31 | public int position() { 32 | return position; 33 | } 34 | 35 | public void setPosition(int position) { 36 | this.position = position; 37 | } 38 | 39 | public void writeRawByte(byte b) { 40 | payload[position++] = b; 41 | } 42 | 43 | public void checkFull() { 44 | if (position != payload.length) { 45 | notFull(); 46 | } 47 | } 48 | 49 | public byte[] payload() { 50 | checkFull(); 51 | return payload; 52 | } 53 | 54 | private void notFull() { 55 | throw new IllegalStateException("wrote " + position + " bytes, expected " + payload.length); 56 | } 57 | 58 | public void writeVarL(long i) { 59 | doWriteVar8(zigzag8(i)); 60 | } 61 | 62 | public void writeVar1(int i) { 63 | payload[position++] = (byte) (2 * i + 1); 64 | } 65 | 66 | public void writeVarUL(long i) { 67 | doWriteVar8(i); 68 | } 69 | 70 | public void writeVarUI(int i) { 71 | doWriteVar4(i); 72 | } 73 | 74 | public void writeVarB(byte b) { 75 | writeVarI(b); 76 | } 77 | 78 | public void writeVarS(short s) { 79 | writeVarI(s); 80 | } 81 | 82 | public void writeVarI(int i) { 83 | doWriteVar4(zigzag4(i)); 84 | } 85 | 86 | public void writeBool(boolean b) { 87 | payload[position++] = b ? BOOL_TRUE : BOOL_FALSE; 88 | } 89 | 90 | private void doWriteVar4(int i) { 91 | // TODO: don't be lazy 92 | doWriteVar8(Integer.toUnsignedLong(i)); 93 | } 94 | 95 | private void doWriteVar8(long i) { 96 | int bits = 64 - Long.numberOfLeadingZeros(i | 1); 97 | if (bits < 8) { 98 | payload[position++] = (byte) (2 * i + 1); 99 | } else if (bits > 56) { 100 | payload[position++] = 0; 101 | write8(i); 102 | } else { 103 | writeVar8Slow0(bits, i); 104 | } 105 | } 106 | 107 | private void writeVar8Slow0(int bits, long i) { 108 | int bytes = 1 + (bits - 1) / 7; 109 | i = (2 * i + 1) << (bytes - 1); 110 | for (int l = 0; l < bytes; l++) { 111 | payload[position++] = (byte) (i & 0xFF); 112 | i >>>= 8; 113 | } 114 | } 115 | 116 | 117 | public void write4(int i) { 118 | payload[position++] = (byte) (i); 119 | payload[position++] = (byte) ((i >>> 8) & 0xFF); 120 | payload[position++] = (byte) ((i >>> 16) & 0xFF); 121 | payload[position++] = (byte) ((i >>> 24) & 0xFF); 122 | } 123 | 124 | public void write8(long i) { 125 | payload[position++] = (byte) (i); 126 | payload[position++] = (byte) ((i >>> 8) & 0xFF); 127 | payload[position++] = (byte) ((i >>> 16) & 0xFF); 128 | payload[position++] = (byte) ((i >>> 24) & 0xFF); 129 | payload[position++] = (byte) ((i >>> 32) & 0xFF); 130 | payload[position++] = (byte) ((i >>> 40) & 0xFF); 131 | payload[position++] = (byte) ((i >>> 48) & 0xFF); 132 | payload[position++] = (byte) ((i >>> 56) & 0xFF); 133 | } 134 | 135 | public void writeFloat(float f) { 136 | write4(Float.floatToIntBits(f)); 137 | } 138 | 139 | public void writeDouble(double d) { 140 | write8(Double.doubleToLongBits(d)); 141 | } 142 | 143 | public void writeDate(Date d) { 144 | writeDouble(d.getTime() / 1000d); 145 | } 146 | 147 | public void writeInstant(Instant i) { 148 | writeDouble(i.toEpochMilli() / 1000d); 149 | } 150 | 151 | public void writeBytes(ByteBuffer b) { 152 | int len = b.remaining(); 153 | if (b.hasArray()) { 154 | writeBytes(b.array(), b.arrayOffset() + b.position(), len); 155 | } else { 156 | doWriteVar8(encodeByteListLength(len)); 157 | int pos = b.position(); 158 | b.get(payload, position, len); 159 | b.position(pos); 160 | position += len; 161 | } 162 | } 163 | 164 | public void writeString(String s) { 165 | writeBytes(s.getBytes(StandardCharsets.UTF_8)); 166 | } 167 | 168 | public void writeBytes(byte[] b) { 169 | writeBytes(b, 0, b.length); 170 | } 171 | 172 | // todo: writeString with null-check message 173 | public void writeBytes(Object o) { 174 | writeBytes((byte[]) o); 175 | } 176 | 177 | public void writeBytes(byte[] b, int off, int len) { 178 | doWriteVar8(encodeByteListLength(len)); 179 | System.arraycopy(b, off, payload, position, len); 180 | position += len; 181 | } 182 | 183 | public void writeEncodedObject(ByteBuffer b) { 184 | int len = b.remaining(); 185 | if (b.hasArray()) { 186 | writeEncodedObject(b.array(), b.arrayOffset() + b.position(), len); 187 | } else { 188 | int pos = b.position(); 189 | b.get(payload, position, len); 190 | b.position(pos); 191 | position += len; 192 | } 193 | } 194 | 195 | public void writeEncodedObject(byte[] b, int off, int len) { 196 | System.arraycopy(b, off, payload, position, len); 197 | position += len; 198 | } 199 | 200 | private static long zigzag8(long i) { 201 | return (i << 1) ^ (i >> 63); 202 | } 203 | 204 | private static int zigzag4(int i) { 205 | return (i << 1) ^ (i >> 31); 206 | } 207 | 208 | public void writeIntegerList(List list) { 209 | int sz = list.size(); 210 | writeVarUL(encodeVarintListLength(sz)); 211 | for (int i = 0; i < sz; i++) { 212 | writeVarI(list.get(i)); 213 | } 214 | } 215 | 216 | public void writeIntegerList(Integer[] list) { 217 | int sz = list.length; 218 | writeVarUL(encodeVarintListLength(sz)); 219 | for (int i = 0; i < sz; i++) { 220 | writeVarI(list[i]); 221 | } 222 | } 223 | 224 | public void writeByteList(Byte[] list) { 225 | int sz = list.length; 226 | writeVarUL(encodeVarintListLength(sz)); 227 | for (int i = 0; i < sz; i++) { 228 | writeVarI(list[i]); 229 | } 230 | } 231 | 232 | public void writeByteList(List list) { 233 | int sz = list.size(); 234 | writeVarUL(encodeVarintListLength(sz)); 235 | for (int i = 0; i < sz; i++) { 236 | writeVarI(list.get(i)); 237 | } 238 | } 239 | 240 | public void writeShortList(Short[] list) { 241 | int sz = list.length; 242 | writeVarUL(encodeVarintListLength(sz)); 243 | for (int i = 0; i < sz; i++) { 244 | writeVarI(list[i]); 245 | } 246 | } 247 | 248 | public void writeShortList(List list) { 249 | int sz = list.size(); 250 | writeVarUL(encodeVarintListLength(sz)); 251 | for (int i = 0; i < sz; i++) { 252 | writeVarI(list.get(i)); 253 | } 254 | } 255 | 256 | public void writeLongList(List list) { 257 | int sz = list.size(); 258 | writeVarUL(encodeVarintListLength(sz)); 259 | for (int i = 0; i < sz; i++) { 260 | writeVarL(list.get(i)); 261 | } 262 | } 263 | 264 | public void writeLongList(Long[] list) { 265 | int sz = list.length; 266 | writeVarUL(encodeVarintListLength(sz)); 267 | for (int i = 0; i < sz; i++) { 268 | writeVarL(list[i]); 269 | } 270 | } 271 | 272 | public void writeBooleanList(List list) { 273 | int sz = list.size(); 274 | writeVarUL(encodeVarintListLength(sz)); 275 | for (int i = 0; i < sz; i++) { 276 | writeBool(list.get(i)); 277 | } 278 | } 279 | 280 | public void writeBooleanList(Boolean[] list) { 281 | int sz = list.length; 282 | writeVarUL(encodeVarintListLength(sz)); 283 | for (int i = 0; i < sz; i++) { 284 | writeBool(list[i]); 285 | } 286 | } 287 | 288 | public void writeFloatList(List list) { 289 | int sz = list.size(); 290 | writeVarUL(encodeFourBListLength(sz)); 291 | for (int i = 0; i < sz; i++) { 292 | writeFloat(list.get(i)); 293 | } 294 | } 295 | 296 | public void writeFloatList(Float[] list) { 297 | int sz = list.length; 298 | writeVarUL(encodeFourBListLength(sz)); 299 | for (int i = 0; i < sz; i++) { 300 | writeFloat(list[i]); 301 | } 302 | } 303 | 304 | public void writeDoubleList(Double[] list) { 305 | int sz = list.length; 306 | writeVarUL(encodeEightBListLength(sz)); 307 | for (int i = 0; i < sz; i++) { 308 | writeDouble(list[i]); 309 | } 310 | } 311 | 312 | public void writeDoubleList(List list) { 313 | int sz = list.size(); 314 | writeVarUL(encodeEightBListLength(sz)); 315 | for (int i = 0; i < sz; i++) { 316 | writeDouble(list.get(i)); 317 | } 318 | } 319 | 320 | private static final int I_1B = (~0 << 7); 321 | private static final int I_2B = (~0 << 14); 322 | private static final int I_3B = (~0 << 21); 323 | private static final int I_4B = (~0 << 28); 324 | private static final long L_5B = (~0L << 35); 325 | private static final long L_6B = (~0L << 42); 326 | private static final long L_7B = (~0L << 49); 327 | private static final long L_8B = (~0L << 56); 328 | 329 | public static int intSize(int i) { 330 | return uintSize(zigzag4(i)); 331 | } 332 | 333 | public static int intSize(boolean b) { 334 | return 1; 335 | } 336 | 337 | public static int uintSize(int i) { 338 | if ((i & I_1B) == 0) { 339 | return 1; 340 | } else if ((i & I_2B) == 0) { 341 | return 2; 342 | } else if ((i & I_3B) == 0) { 343 | return 3; 344 | } else if ((i & I_4B) == 0) { 345 | return 4; 346 | } else { 347 | return 5; 348 | } 349 | } 350 | 351 | public static int longSize(long i) { 352 | return ulongSize(zigzag8(i)); 353 | } 354 | 355 | public static int longSize(boolean b) { 356 | return 1; 357 | } 358 | 359 | public static int ulongSize(long i) { 360 | if ((i & I_1B) == 0) { 361 | return 1; 362 | } else if ((i & I_2B) == 0) { 363 | return 2; 364 | } else if ((i & I_3B) == 0) { 365 | return 3; 366 | } else if ((i & I_4B) == 0) { 367 | return 4; 368 | } else if ((i & L_5B) == 0) { 369 | return 5; 370 | } else if ((i & L_6B) == 0) { 371 | return 6; 372 | } else if ((i & L_7B) == 0) { 373 | return 7; 374 | } else if ((i & L_8B) == 0) { 375 | return 8; 376 | } else { 377 | return 9; 378 | } 379 | } 380 | 381 | public static int byteListLengthEncodedSize(int len) { 382 | return len + ulongSize(encodeByteListLength(len)); 383 | } 384 | 385 | public static int lenPrefixedListLengthEncodedSize(int size, int elements) { 386 | return size + ulongSize(encodeLenPrefixedListLength(elements)); 387 | } 388 | 389 | public static int byteListLengthEncodedSize(ByteBuffer buf) { 390 | return byteListLengthEncodedSize(buf.remaining()); 391 | } 392 | 393 | public static void missingField(String message) { 394 | throw new NullPointerException(message); 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/Enhancer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import java.io.PrintStream; 9 | import java.nio.ByteBuffer; 10 | import java.time.Instant; 11 | import java.util.*; 12 | import java.util.concurrent.ConcurrentHashMap; 13 | import java.util.stream.Collectors; 14 | import software.amazon.smithy.java.sparrowhawk.KConstants; 15 | import software.amazon.smithy.java.sparrowhawk.SparrowhawkDeserializer; 16 | import software.amazon.smithy.model.Model; 17 | import software.amazon.smithy.model.shapes.ListShape; 18 | import software.amazon.smithy.model.shapes.MapShape; 19 | import software.amazon.smithy.model.shapes.MemberShape; 20 | import software.amazon.smithy.model.shapes.Shape; 21 | import software.amazon.smithy.model.shapes.ShapeId; 22 | import software.amazon.smithy.model.shapes.ShapeType; 23 | import software.amazon.smithy.model.shapes.StructureShape; 24 | import software.amazon.smithy.protocol.traits.IdxTrait; 25 | 26 | /** 27 | * A reflective Sparrowhawk payload printer for debugging purposes. Uses a Smithy model to know how to decode 28 | * Sparrowhawk payloads (instead of seeing them as a bunch of byte lists). 29 | */ 30 | @SuppressWarnings("unused") 31 | public final class Enhancer { 32 | 33 | private final Model model; 34 | private final PrintStream output; 35 | private final Map>> memberCache = new ConcurrentHashMap<>(); 36 | 37 | /** 38 | * @param model the Smithy model for the payloads this Enhancer can decode 39 | */ 40 | public Enhancer(Model model) { 41 | this(model, System.err); 42 | } 43 | 44 | /** 45 | * @param model the Smithy model for the payloads this Enhancer can decode 46 | * @param output a PrintStream to write the decoded payload to 47 | */ 48 | public Enhancer(Model model, PrintStream output) { 49 | this.model = model; 50 | this.output = output; 51 | } 52 | 53 | /** 54 | * Dumps a Sparrowhawk-serialized Smithy shape in human-readable format. 55 | * 56 | * @param payload a raw Sparrowhawk payload 57 | * @param shape the shape used to decode this payload. Can be of any type 58 | */ 59 | public void enhance(byte[] payload, Shape shape) { 60 | try { 61 | enhanceNext(new SparrowhawkDeserializer(payload), shape, 0); 62 | } catch (BadInputException e) { 63 | printWithIndent(0, "[ERROR (idx: %d)] %s", e.pos, e.getMessage()); 64 | } 65 | } 66 | 67 | private void enhanceNext(SparrowhawkDeserializer deser, Shape shape, int level) { 68 | if (shape instanceof StructureShape) { 69 | int type = deser.varUI(); 70 | if ((type & 1) != 0) { 71 | throw new BadInputException(deser.pos(), "Invalid struct (encountered type %d)", type & 7); 72 | } 73 | int structLen = type >> 1; 74 | if (structLen == 0) { 75 | printWithIndent(level, "[Struct (size: %d, type: %s)][/Struct]", structLen, shape.getId()); 76 | return; 77 | } 78 | printWithIndent(level, "[Struct (size: %d, type: %s)]", structLen, shape.getId()); 79 | enhanceStruct(deser, (StructureShape) shape, level + 1, structLen); 80 | printWithIndent(level, "[/Struct]"); 81 | } else if (shape instanceof ListShape) { 82 | long len = KConstants.decodeElementCount(deser.varUL()); 83 | Shape member = model.expectShape(((ListShape) shape).getMember().getTarget()); 84 | if (len == 0) { 85 | printWithIndent(level, "[List (size: %d, valueType: %s)][/List]", len, member.getId()); 86 | return; 87 | } 88 | printWithIndent(level, "[List (size: %d, valueType: %s)]", len, member.getId()); 89 | for (int i = 0; i < len; i++) { 90 | enhanceNext(deser, member, level + 1); 91 | } 92 | printWithIndent(level, "[/List]"); 93 | } else if (shape instanceof MapShape) { 94 | Shape valueShape = model.expectShape(((MapShape) shape).getValue().getTarget()); 95 | 96 | int type = deser.varUI(); 97 | if ((type & 1) != 0) { 98 | throw new BadInputException(deser.pos(), "Invalid map (encountered type %d)", type & 7); 99 | } 100 | int structLen = type >> 1; 101 | if (structLen == 0) { 102 | printWithIndent( 103 | level, 104 | "[Map (size: %d, keys: %d, valueType: %s)][/Map]", 105 | structLen, 106 | 0, 107 | valueShape.getId() 108 | ); 109 | return; 110 | } 111 | 112 | long typeSectionHeader = deser.varUL(); 113 | int fieldType = (int) (typeSectionHeader & 3); 114 | if (fieldType != KConstants.T_LIST) { 115 | throw new BadInputException( 116 | deser.pos(), 117 | "Structure field type MUST be %d, was %d", 118 | KConstants.T_LIST, 119 | fieldType 120 | ); 121 | } 122 | if ((typeSectionHeader & 4) != 0) { 123 | throw new BadInputException(deser.pos(), "Maps should not have continuation bit set"); 124 | } 125 | 126 | var fieldsSet = new HashSet(); 127 | for (int i = 0; i < 61; i++) { 128 | if ((typeSectionHeader >> (3 + i) & 1) == 1) { 129 | fieldsSet.add(i); 130 | } 131 | } 132 | 133 | if (!fieldsSet.equals(Set.of(0, 1))) { 134 | throw new BadInputException( 135 | deser.pos(), 136 | "Non-empty maps must have fields 0 and 1 set, found %s", 137 | fieldsSet 138 | ); 139 | } 140 | 141 | int keyLen = (int) KConstants.decodeElementCount(deser.varUL()); 142 | List keys = new ArrayList<>(); 143 | for (int i = 0; i < keyLen; i++) { 144 | keys.add(deser.string()); 145 | } 146 | int valueLen = (int) KConstants.decodeElementCount(deser.varUL()); 147 | if (keyLen != valueLen) { 148 | throw new BadInputException( 149 | deser.pos(), 150 | "Maps must have key and value length lists of same size. " + 151 | "Keys length: %d; Values length: %d", 152 | keyLen, 153 | valueLen 154 | ); 155 | } 156 | printWithIndent( 157 | level, 158 | "[Map (size: %d, keys: %d, valueType: %s)]", 159 | structLen, 160 | keyLen, 161 | valueShape.getId() 162 | ); 163 | for (int i = 0; i < keyLen; i++) { 164 | if (valueShape.getType().getCategory() == ShapeType.Category.SIMPLE) { 165 | printWithIndent( 166 | level + 1, 167 | "[Entry (key: %s)]%s[/Entry]", 168 | keys.get(i), 169 | getSimpleValue(deser, valueShape) 170 | ); 171 | } else { 172 | printWithIndent(level + 1, "[Entry (key: %s)]", keys.get(i)); 173 | enhanceNext(deser, valueShape, level + 2); 174 | printWithIndent(level + 1, "[/Entry]", keys.get(i)); 175 | } 176 | } 177 | } else if (shape.getType().getCategory() == ShapeType.Category.SIMPLE) { 178 | printWithIndent(level, "[> %s]", getSimpleValue(deser, shape)); 179 | } else { 180 | throw new UnsupportedOperationException(shape.toString()); 181 | } 182 | } 183 | 184 | private void printWithIndent(int level, String s, Object... args) { 185 | for (int i = 0; i < level; i++) { output.print(" "); } 186 | output.printf((s) + "%n", args); 187 | } 188 | 189 | private void enhanceStruct(SparrowhawkDeserializer deser, StructureShape shape, int level, long len) { 190 | long end = deser.pos() + len; 191 | while (deser.pos() < end) { 192 | long typeSectionHeader = deser.varUL(); 193 | int fieldType = (int) (typeSectionHeader & 3); 194 | 195 | int offset = 0; 196 | if ((typeSectionHeader & 4) != 0) { 197 | offset = deser.varUI() + 1; 198 | } 199 | 200 | var fieldsSet = new LinkedHashSet(); 201 | for (int i = 0; i < 61; i++) { 202 | if ((typeSectionHeader >> (3 + i) & 1) == 1) { 203 | fieldsSet.add(i + (offset * 61)); 204 | } 205 | } 206 | 207 | printWithIndent( 208 | level, 209 | "[TypeSection (type: %s, fields: %s)]", 210 | typeSectionTypeToString(deser, fieldType), 211 | fieldsSet 212 | ); 213 | 214 | for (Integer field : fieldsSet) { 215 | MemberShape member = getSmithyMember(shape, fieldType, field); 216 | Shape target = model.expectShape(member.getTarget()); 217 | if (target.getType().getCategory() == ShapeType.Category.SIMPLE) { 218 | printWithIndent( 219 | level + 1, 220 | "[Member (name: %s, kIdx: %d, sIdx: %d, type: %s)]%s[/Member]", 221 | member.getMemberName(), 222 | field, 223 | member.expectTrait(IdxTrait.class).getValue(), 224 | target.getId(), 225 | getSimpleValue(deser, target) 226 | ); 227 | } else { 228 | printWithIndent( 229 | level + 1, 230 | "[Member (name: %s, kIdx: %d, sIdx: %d, type: %s)]", 231 | member.getMemberName(), 232 | field, 233 | member.expectTrait(IdxTrait.class).getValue(), 234 | target.getId() 235 | ); 236 | enhanceNext(deser, target, level + 2); 237 | printWithIndent(level + 1, "[/Member]"); 238 | } 239 | } 240 | printWithIndent(level, "[/TypeSection]"); 241 | } 242 | } 243 | 244 | private static byte[] array(ByteBuffer buffer) { 245 | byte[] array = new byte[buffer.remaining()]; 246 | buffer.get(array); 247 | return array; 248 | } 249 | 250 | private Object getSimpleValue(SparrowhawkDeserializer deser, Shape target) { 251 | switch (target.getType()) { 252 | case BLOB -> { 253 | return HexFormat.ofDelimiter(":").formatHex(array(deser.bytes())); 254 | } 255 | case BOOLEAN -> { 256 | return deser.bool(); 257 | } 258 | case STRING, ENUM -> { 259 | return deser.string(); 260 | } 261 | case TIMESTAMP -> { 262 | return Instant.ofEpochMilli((long) (deser.d8() * 1000L)).toString(); 263 | } 264 | case BYTE -> { 265 | return (byte) deser.varI(); 266 | } 267 | case SHORT -> { 268 | return (short) deser.varI(); 269 | } 270 | case INTEGER, INT_ENUM -> { 271 | return deser.varI(); 272 | } 273 | case LONG -> { 274 | return deser.varL(); 275 | } 276 | case FLOAT -> { 277 | return deser.f4(); 278 | } 279 | case DOUBLE -> { 280 | return deser.d8(); 281 | } 282 | default -> throw new IllegalArgumentException(); 283 | } 284 | } 285 | 286 | private MemberShape getSmithyMember(StructureShape shape, int fieldType, Integer field) { 287 | Map> structureMap = memberCache.computeIfAbsent( 288 | shape.getId(), 289 | (unused) -> populateMemberCache(shape) 290 | ); 291 | 292 | return structureMap.getOrDefault(fieldType, Collections.emptyMap()).get(field); 293 | } 294 | 295 | private Map> populateMemberCache(StructureShape shape) { 296 | final Map> byType = shape.members() 297 | .stream() 298 | .collect(Collectors.groupingBy((ms) -> { 299 | switch (model.expectShape(ms.getTarget()).getType()) { 300 | case BLOB, STRING, DOCUMENT, ENUM, LIST, SET, MAP, STRUCTURE, UNION -> { 301 | return KConstants.T_LIST; 302 | } 303 | case BOOLEAN, BYTE, SHORT, INTEGER, LONG, INT_ENUM -> { 304 | return KConstants.T_VARINT; 305 | } 306 | case TIMESTAMP, DOUBLE -> { 307 | return KConstants.T_EIGHT; 308 | } 309 | case FLOAT -> { 310 | return KConstants.T_FOUR; 311 | } 312 | default -> throw new UnsupportedOperationException(); 313 | } 314 | })); 315 | 316 | final Map> retVal = new HashMap<>(); 317 | for (Map.Entry> e : byType.entrySet()) { 318 | List ordered = new ArrayList<>(e.getValue()); 319 | ordered.sort(Comparator.comparingInt(ms -> ms.expectTrait(IdxTrait.class).getValue())); 320 | final Map newIdxed = new HashMap<>(); 321 | for (int i = 0; i < ordered.size(); i++) { 322 | newIdxed.put(i, ordered.get(i)); 323 | } 324 | retVal.put(e.getKey(), Collections.unmodifiableMap(newIdxed)); 325 | } 326 | return Collections.unmodifiableMap(retVal); 327 | } 328 | 329 | private String typeSectionTypeToString(SparrowhawkDeserializer deser, int i) { 330 | return switch (i) { 331 | case 0 -> "lists"; 332 | case 1 -> "varints"; 333 | case 2 -> "four-byte items"; 334 | case 3 -> "eight-byte items"; 335 | default -> throw new BadInputException(deser.pos(), "Unknown type %d", i); 336 | }; 337 | } 338 | 339 | private static final class BadInputException extends RuntimeException { 340 | private final int pos; 341 | 342 | public BadInputException(String message, int pos) { 343 | super(message); 344 | this.pos = pos; 345 | } 346 | 347 | public BadInputException(String message, Throwable cause, int pos) { 348 | super(message, cause); 349 | this.pos = pos; 350 | } 351 | 352 | public BadInputException(int pos, String message, Object... args) { 353 | this(String.format(message, args), pos); 354 | } 355 | } 356 | 357 | } 358 | -------------------------------------------------------------------------------- /sparrowhawk-codegen/src/main/java/software/amazon/smithy/sparrowhawk/codegen/SparrowhawkSymbolVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package software.amazon.smithy.sparrowhawk.codegen; 7 | 8 | import java.io.File; 9 | import software.amazon.smithy.codegen.core.CodegenException; 10 | import software.amazon.smithy.codegen.core.ReservedWordSymbolProvider; 11 | import software.amazon.smithy.codegen.core.ReservedWordsBuilder; 12 | import software.amazon.smithy.codegen.core.Symbol; 13 | import software.amazon.smithy.codegen.core.SymbolProvider; 14 | import software.amazon.smithy.codegen.core.SymbolReference; 15 | import software.amazon.smithy.java.sparrowhawk.BooleanMap; 16 | import software.amazon.smithy.java.sparrowhawk.ByteMap; 17 | import software.amazon.smithy.java.sparrowhawk.DoubleMap; 18 | import software.amazon.smithy.java.sparrowhawk.FloatMap; 19 | import software.amazon.smithy.java.sparrowhawk.IntegerMap; 20 | import software.amazon.smithy.java.sparrowhawk.LongMap; 21 | import software.amazon.smithy.java.sparrowhawk.ShortMap; 22 | import software.amazon.smithy.java.sparrowhawk.SparseStructureMap; 23 | import software.amazon.smithy.java.sparrowhawk.StringMap; 24 | import software.amazon.smithy.java.sparrowhawk.StructureMap; 25 | import software.amazon.smithy.model.Model; 26 | import software.amazon.smithy.model.shapes.BigDecimalShape; 27 | import software.amazon.smithy.model.shapes.BigIntegerShape; 28 | import software.amazon.smithy.model.shapes.BlobShape; 29 | import software.amazon.smithy.model.shapes.BooleanShape; 30 | import software.amazon.smithy.model.shapes.ByteShape; 31 | import software.amazon.smithy.model.shapes.DocumentShape; 32 | import software.amazon.smithy.model.shapes.DoubleShape; 33 | import software.amazon.smithy.model.shapes.FloatShape; 34 | import software.amazon.smithy.model.shapes.IntEnumShape; 35 | import software.amazon.smithy.model.shapes.IntegerShape; 36 | import software.amazon.smithy.model.shapes.ListShape; 37 | import software.amazon.smithy.model.shapes.LongShape; 38 | import software.amazon.smithy.model.shapes.MapShape; 39 | import software.amazon.smithy.model.shapes.MemberShape; 40 | import software.amazon.smithy.model.shapes.OperationShape; 41 | import software.amazon.smithy.model.shapes.ResourceShape; 42 | import software.amazon.smithy.model.shapes.ServiceShape; 43 | import software.amazon.smithy.model.shapes.Shape; 44 | import software.amazon.smithy.model.shapes.ShapeType; 45 | import software.amazon.smithy.model.shapes.ShapeVisitor; 46 | import software.amazon.smithy.model.shapes.ShortShape; 47 | import software.amazon.smithy.model.shapes.StringShape; 48 | import software.amazon.smithy.model.shapes.StructureShape; 49 | import software.amazon.smithy.model.shapes.TimestampShape; 50 | import software.amazon.smithy.model.shapes.UnionShape; 51 | import software.amazon.smithy.model.traits.SparseTrait; 52 | import software.amazon.smithy.model.traits.UniqueItemsTrait; 53 | import software.amazon.smithy.utils.StringUtils; 54 | 55 | public class SparrowhawkSymbolVisitor implements SymbolProvider, ShapeVisitor { 56 | 57 | private final Model model; 58 | private final ReservedWordSymbolProvider.Escaper escaper; 59 | private final ServiceShape service; 60 | private final SparrowhawkSettings sparrowhawkSettings; 61 | 62 | public SparrowhawkSymbolVisitor(Model model, ServiceShape service, SparrowhawkSettings sparrowhawkSettings) { 63 | this.model = model; 64 | this.service = service; 65 | this.sparrowhawkSettings = sparrowhawkSettings; 66 | 67 | // Load reserved words from new-line delimited files. 68 | var reservedWords = new ReservedWordsBuilder() 69 | .loadWords(SparrowhawkSymbolVisitor.class.getResource("reserved-words.txt"), this::escapeWord) 70 | .build(); 71 | 72 | escaper = ReservedWordSymbolProvider.builder() 73 | .nameReservedWords(reservedWords) 74 | .memberReservedWords(reservedWords) 75 | // Only escape words when the symbol has a definition file to 76 | // prevent escaping intentional references to built-in types. 77 | .escapePredicate((shape, symbol) -> !StringUtils.isEmpty(symbol.getDefinitionFile())) 78 | .buildEscaper(); 79 | 80 | } 81 | 82 | protected final Model model() { 83 | return model; 84 | } 85 | 86 | protected final ReservedWordSymbolProvider.Escaper escaper() { 87 | return escaper; 88 | } 89 | 90 | protected final ServiceShape service() { 91 | return service; 92 | } 93 | 94 | private String escapeWord(String word) { 95 | return "_" + word; 96 | } 97 | 98 | @Override 99 | public Symbol toSymbol(Shape shape) { 100 | Symbol symbol = shape.accept(this); 101 | return escaper.escapeSymbol(shape, symbol); 102 | } 103 | 104 | @Override 105 | public String toMemberName(MemberShape shape) { 106 | return escaper.escapeMemberName(StringUtils.uncapitalize(shape.getMemberName())); 107 | } 108 | 109 | @Override 110 | public Symbol blobShape(BlobShape shape) { 111 | return createSymbolBuilder(shape, "ByteBuffer", "java.nio").build(); 112 | } 113 | 114 | @Override 115 | public Symbol booleanShape(BooleanShape shape) { 116 | return createSymbolBuilder(shape, "Boolean").build(); 117 | } 118 | 119 | @Override 120 | public Symbol listShape(ListShape shape) { 121 | Shape memberShape = model.expectShape(shape.getMember().getTarget()); 122 | var valueSymbol = toSymbol(memberShape); 123 | Symbol.Builder b = listSymbolBuilder(shape); 124 | b.putProperty("value", valueSymbol).addReference(valueSymbol); 125 | if (memberShape.isStringShape()) { 126 | b.putProperty("sparrowhawkField", Symbol.builder().name("Object").build()); 127 | } 128 | return b.build(); 129 | } 130 | 131 | protected Symbol.Builder listSymbolBuilder(ListShape shape) { 132 | if (shape.hasTrait(UniqueItemsTrait.class)) { 133 | return createSymbolBuilder(shape, "Set", "java.util"); 134 | } else { 135 | return createSymbolBuilder(shape, "List", "java.util"); 136 | } 137 | } 138 | 139 | @Override 140 | public Symbol mapShape(MapShape shape) { 141 | var keySymbol = toSymbol(model.expectShape(shape.getKey().getTarget())); 142 | var valueSymbol = toSymbol(model.expectShape(shape.getValue().getTarget())); 143 | Class sparrowhawkCollectionImpl = switch (model.expectShape(shape.getValue().getTarget()).getType()) { 144 | case BOOLEAN -> BooleanMap.class; 145 | case STRING, ENUM -> StringMap.class; 146 | case BYTE -> ByteMap.class; 147 | case SHORT -> ShortMap.class; 148 | case INTEGER, INT_ENUM -> IntegerMap.class; 149 | case LONG -> LongMap.class; 150 | case FLOAT -> FloatMap.class; 151 | case DOUBLE -> DoubleMap.class; 152 | case MAP, LIST, STRUCTURE, UNION -> shape.hasTrait(SparseTrait.class) 153 | ? SparseStructureMap.class 154 | : StructureMap.class; 155 | default -> throw new IllegalArgumentException( 156 | model.expectShape(shape.getValue().getTarget()) 157 | .getType() 158 | .toString() 159 | ); 160 | }; 161 | 162 | SymbolReference sparrowhawkCollectionSymbol = Symbol.builder() 163 | .namespace(sparrowhawkCollectionImpl.getPackageName(), ".") 164 | .name(sparrowhawkCollectionImpl.getSimpleName()) 165 | .build() 166 | .toReference(null); 167 | return mapSymbolBuilder(shape) 168 | .putProperty("key", keySymbol) 169 | .putProperty("value", valueSymbol) 170 | .putProperty("sparrowhawkField", Symbol.builder().name("Object").build()) 171 | .putProperty("sparrowhawkCollection", sparrowhawkCollectionSymbol) 172 | .addReference(keySymbol) 173 | .addReference(valueSymbol) 174 | .addReference(sparrowhawkCollectionSymbol) 175 | .build(); 176 | } 177 | 178 | protected Symbol.Builder mapSymbolBuilder(MapShape shape) { 179 | return createSymbolBuilder(shape, "Map", "java.util"); 180 | } 181 | 182 | @Override 183 | public Symbol byteShape(ByteShape shape) { 184 | return createSymbolBuilder(shape, "Byte").build(); 185 | } 186 | 187 | @Override 188 | public Symbol shortShape(ShortShape shape) { 189 | return createSymbolBuilder(shape, "Short").build(); 190 | } 191 | 192 | @Override 193 | public Symbol integerShape(IntegerShape shape) { 194 | return createSymbolBuilder(shape, "Integer").build(); 195 | } 196 | 197 | @Override 198 | public Symbol longShape(LongShape shape) { 199 | return createSymbolBuilder(shape, "Long").build(); 200 | } 201 | 202 | @Override 203 | public Symbol floatShape(FloatShape shape) { 204 | return createSymbolBuilder(shape, "Float").build(); 205 | } 206 | 207 | @Override 208 | public Symbol documentShape(DocumentShape shape) { 209 | throw new IllegalArgumentException("Documents are not currently supported"); 210 | } 211 | 212 | @Override 213 | public Symbol doubleShape(DoubleShape shape) { 214 | return createSymbolBuilder(shape, "Double").build(); 215 | } 216 | 217 | @Override 218 | public Symbol bigIntegerShape(BigIntegerShape shape) { 219 | return createSymbolBuilder(shape, "BigInteger", "java.math").build(); 220 | } 221 | 222 | @Override 223 | public Symbol bigDecimalShape(BigDecimalShape shape) { 224 | return createSymbolBuilder(shape, "BigDecimal", "java.math").build(); 225 | } 226 | 227 | @Override 228 | public Symbol operationShape(OperationShape shape) { 229 | return createSymbolBuilder(shape, shape.getId().getName(), shape.getId().getNamespace()).build(); 230 | } 231 | 232 | @Override 233 | public Symbol resourceShape(ResourceShape shape) { 234 | return createSymbolBuilder(shape, shape.getId().getName(), shape.getId().getNamespace()).build(); 235 | } 236 | 237 | @Override 238 | public Symbol serviceShape(ServiceShape shape) { 239 | return createSymbolBuilder(shape, shape.getId().getName(), shape.getId().getNamespace()).build(); 240 | } 241 | 242 | @Override 243 | public Symbol stringShape(StringShape shape) { 244 | return createSymbolBuilder(shape, "String") 245 | .putProperty("sparrowhawkField", Symbol.builder().name("Object").build()) 246 | .build(); 247 | } 248 | 249 | private String getDefaultShapeName(Shape shape) { 250 | // Use the service-aliased name 251 | return "Sparrowhawk" + StringUtils.capitalize(shape.getId().getName(service)); 252 | } 253 | 254 | @Override 255 | public Symbol structureShape(StructureShape shape) { 256 | Symbol base = structureSymbolBuilder(shape).build(); 257 | return base.toBuilder() 258 | .definitionFile( 259 | base.getNamespace().replaceAll("\\.", File.separator) 260 | + File.separator + base.getName() + ".java" 261 | ) 262 | .build(); 263 | } 264 | 265 | protected Symbol.Builder structureSymbolBuilder(StructureShape shape) { 266 | return createSymbolBuilder(shape, getDefaultShapeName(shape), shape.getId().getNamespace() + ".sparrowhawk"); 267 | } 268 | 269 | @Override 270 | public Symbol unionShape(UnionShape shape) { 271 | Symbol base = unionSymbolBuilder(shape).build(); 272 | return base.toBuilder() 273 | .definitionFile( 274 | base.getNamespace().replaceAll("\\.", File.separator) 275 | + File.separator + base.getName() + ".java" 276 | ) 277 | .build(); 278 | } 279 | 280 | protected Symbol.Builder unionSymbolBuilder(UnionShape shape) { 281 | return createSymbolBuilder(shape, getDefaultShapeName(shape), shape.getId().getNamespace() + ".sparrowhawk"); 282 | } 283 | 284 | @Override 285 | public Symbol memberShape(MemberShape shape) { 286 | Shape targetShape = model.getShape(shape.getTarget()) 287 | .orElseThrow(() -> new CodegenException("Shape not found: " + shape.getTarget())); 288 | 289 | Symbol targetSymbol = null; 290 | if (targetShape.getType().getCategory() == ShapeType.Category.SIMPLE) { 291 | targetSymbol = primitive(targetShape); 292 | } 293 | 294 | if (targetSymbol == null) { 295 | targetSymbol = toSymbol(targetShape); 296 | } 297 | return targetSymbol.toBuilder() 298 | .putProperty("memberShape", shape) 299 | .putProperty("methodName", StringUtils.capitalize(shape.getMemberName())) 300 | .build(); 301 | } 302 | 303 | private Symbol primitive(Shape targetShape) { 304 | switch (targetShape.getType()) { 305 | case BOOLEAN -> { 306 | return createSymbolBuilder(targetShape, "boolean") 307 | .putProperty("boxed", booleanShape((BooleanShape) targetShape)) 308 | .build(); 309 | } 310 | case BYTE -> { 311 | return createSymbolBuilder(targetShape, "byte") 312 | .putProperty("boxed", byteShape((ByteShape) targetShape)) 313 | .build(); 314 | } 315 | case SHORT -> { 316 | return createSymbolBuilder(targetShape, "short") 317 | .putProperty("boxed", shortShape((ShortShape) targetShape)) 318 | .build(); 319 | } 320 | case INTEGER -> { 321 | return createSymbolBuilder(targetShape, "int") 322 | .putProperty("boxed", integerShape((IntegerShape) targetShape)) 323 | .build(); 324 | } 325 | case INT_ENUM -> { 326 | return createSymbolBuilder(targetShape, "int") 327 | .putProperty("boxed", intEnumShape((IntEnumShape) targetShape)) 328 | .build(); 329 | } 330 | case LONG -> { 331 | return createSymbolBuilder(targetShape, "long") 332 | .putProperty("boxed", longShape((LongShape) targetShape)) 333 | .build(); 334 | } 335 | case FLOAT -> { 336 | return createSymbolBuilder(targetShape, "float") 337 | .putProperty("boxed", floatShape((FloatShape) targetShape)) 338 | .build(); 339 | } 340 | case DOUBLE -> { 341 | return createSymbolBuilder(targetShape, "double") 342 | .putProperty("boxed", doubleShape((DoubleShape) targetShape)) 343 | .build(); 344 | } 345 | } 346 | return null; 347 | } 348 | 349 | @Override 350 | public Symbol timestampShape(TimestampShape shape) { 351 | if (sparrowhawkSettings.useInstant()) { 352 | return createSymbolBuilder(shape, "Instant", "java.time").build(); 353 | } 354 | return createSymbolBuilder(shape, "Date", "java.util").build(); 355 | } 356 | 357 | protected Symbol.Builder createSymbolBuilder(Shape shape, String typeName) { 358 | return Symbol.builder().putProperty("shape", shape).name(typeName); 359 | } 360 | 361 | protected Symbol.Builder createSymbolBuilder(Shape shape, String typeName, String namespace) { 362 | return createSymbolBuilder(shape, typeName).namespace(namespace, "."); 363 | } 364 | } 365 | --------------------------------------------------------------------------------