From 2a8e2137037ff130938cd3643772b405798b72c4 Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Fri, 2 Aug 2024 12:49:11 -0400 Subject: [PATCH 01/21] [Go] SBE implementation in Go using flyweights #765 To generate flyweights instead of structs, set the following flag: ``` sbe.go.generate.generate.flyweights=true ``` --- .gitignore | 75 +- README.md | 2 + build.gradle | 98 +- gocode/Makefile | 49 +- gocode/README.md | 10 +- gocode/go.mod | 7 + gocode/go.sum | 7 + .../flyweight/baseline-bigendian/Car_test.go | 146 + gocode/src/flyweight/baseline/Car_test.go | 138 + .../flyweight/basic/SBE tests/ENUM_test.go | 12 + .../basic/SBE tests/Message1_test.go | 29 + .../src/flyweight/basic/SBE tests/SET_test.go | 28 + .../src/flyweight/composite/Composite_test.go | 44 + .../composite_elements/Message_test.go | 91 + .../example-schema/CarExample_test.go | 572 +++ .../group/SBE tests/TestMessage1_test.go | 55 + .../group_with_data/TestMessages_test.go | 200 + .../Extension_test.go | 210 ++ .../src/flyweight/issue435/Issue435_test.go | 48 + .../src/flyweight/issue472/Issue472_test.go | 27 + .../src/flyweight/issue483/Issue483_test.go | 35 + .../src/flyweight/issue488/Issue488_test.go | 9 + .../src/flyweight/issue560/Issue560_test.go | 28 + .../{ => flyweight}/issue847/issue847_test.go | 0 .../{ => flyweight}/issue848/issue848_test.go | 0 .../{ => flyweight}/issue849/issue849_test.go | 0 .../src/{ => flyweight}/mktdata/fixme_test.go | 0 gocode/src/flyweight/simple/simple_test.go | 46 + .../since-deprecated/SinceDeprecated_test.go | 23 + .../vardata/SBE tests/TestMessage1_test.go | 27 + .../baseline-bigendian/Car_test.go | 0 gocode/src/{ => struct}/baseline/Car_test.go | 0 .../{ => struct}/basic/SBE tests/ENUM_test.go | 0 .../basic/SBE tests/Message1_test.go | 0 .../{ => struct}/basic/SBE tests/SET_test.go | 0 .../{ => struct}/composite/Composite_test.go | 0 .../composite_elements/Message_test.go | 0 .../{ => struct}/example-schema/CarExample.go | 0 .../example-schema/CarExample_test.go | 0 .../clientserver.go | 0 .../group/SBE tests/TestMessage1_test.go | 0 .../group_with_data/TestMessages_test.go | 0 .../Extension_test.go | 2 +- .../{ => struct}/issue435/Issue435_test.go | 0 .../{ => struct}/issue472/Issue472_test.go | 0 .../{ => struct}/issue483/Issue483_test.go | 0 .../{ => struct}/issue488/Issue488_test.go | 0 .../{ => struct}/issue560/Issue560_test.go | 0 gocode/src/struct/issue847/issue847_test.go | 9 + gocode/src/struct/issue848/issue848_test.go | 9 + gocode/src/struct/issue849/issue849_test.go | 9 + gocode/src/struct/mktdata/fixme_test.go | 10 + .../{ => struct}/mktdata/preallocated_test.go | 0 gocode/src/{ => struct}/simple/simple_test.go | 0 .../since-deprecated/SinceDeprecated_test.go | 0 .../vardata/SBE tests/TestMessage1_test.go | 0 .../java/uk/co/real_logic/sbe/SbeTool.java | 6 + .../generation/TargetCodeGeneratorLoader.java | 20 +- .../flyweight/GolangFlyweightGenerator.java | 3286 +++++++++++++++++ .../GolangFlyweightOutputManager.java | 72 + .../golang/flyweight/GolangFlyweightUtil.java | 224 ++ .../golang/{ => struct}/GolangGenerator.java | 4 +- .../{ => struct}/GolangOutputManager.java | 2 +- .../golang/{ => struct}/GolangUtil.java | 2 +- 64 files changed, 5615 insertions(+), 56 deletions(-) create mode 100644 gocode/go.mod create mode 100644 gocode/go.sum create mode 100644 gocode/src/flyweight/baseline-bigendian/Car_test.go create mode 100644 gocode/src/flyweight/baseline/Car_test.go create mode 100644 gocode/src/flyweight/basic/SBE tests/ENUM_test.go create mode 100644 gocode/src/flyweight/basic/SBE tests/Message1_test.go create mode 100644 gocode/src/flyweight/basic/SBE tests/SET_test.go create mode 100644 gocode/src/flyweight/composite/Composite_test.go create mode 100644 gocode/src/flyweight/composite_elements/Message_test.go create mode 100644 gocode/src/flyweight/example-schema/CarExample_test.go create mode 100644 gocode/src/flyweight/group/SBE tests/TestMessage1_test.go create mode 100644 gocode/src/flyweight/group_with_data/TestMessages_test.go create mode 100644 gocode/src/flyweight/group_with_data_extension/Extension_test.go create mode 100644 gocode/src/flyweight/issue435/Issue435_test.go create mode 100644 gocode/src/flyweight/issue472/Issue472_test.go create mode 100644 gocode/src/flyweight/issue483/Issue483_test.go create mode 100644 gocode/src/flyweight/issue488/Issue488_test.go create mode 100644 gocode/src/flyweight/issue560/Issue560_test.go rename gocode/src/{ => flyweight}/issue847/issue847_test.go (100%) rename gocode/src/{ => flyweight}/issue848/issue848_test.go (100%) rename gocode/src/{ => flyweight}/issue849/issue849_test.go (100%) rename gocode/src/{ => flyweight}/mktdata/fixme_test.go (100%) create mode 100644 gocode/src/flyweight/simple/simple_test.go create mode 100644 gocode/src/flyweight/since-deprecated/SinceDeprecated_test.go create mode 100644 gocode/src/flyweight/vardata/SBE tests/TestMessage1_test.go rename gocode/src/{ => struct}/baseline-bigendian/Car_test.go (100%) rename gocode/src/{ => struct}/baseline/Car_test.go (100%) rename gocode/src/{ => struct}/basic/SBE tests/ENUM_test.go (100%) rename gocode/src/{ => struct}/basic/SBE tests/Message1_test.go (100%) rename gocode/src/{ => struct}/basic/SBE tests/SET_test.go (100%) rename gocode/src/{ => struct}/composite/Composite_test.go (100%) rename gocode/src/{ => struct}/composite_elements/Message_test.go (100%) rename gocode/src/{ => struct}/example-schema/CarExample.go (100%) rename gocode/src/{ => struct}/example-schema/CarExample_test.go (100%) rename gocode/src/{ => struct}/example-socket-clientserver/clientserver.go (100%) rename gocode/src/{ => struct}/group/SBE tests/TestMessage1_test.go (100%) rename gocode/src/{ => struct}/group_with_data/TestMessages_test.go (100%) rename gocode/src/{ => struct}/group_with_data_extension/Extension_test.go (99%) rename gocode/src/{ => struct}/issue435/Issue435_test.go (100%) rename gocode/src/{ => struct}/issue472/Issue472_test.go (100%) rename gocode/src/{ => struct}/issue483/Issue483_test.go (100%) rename gocode/src/{ => struct}/issue488/Issue488_test.go (100%) rename gocode/src/{ => struct}/issue560/Issue560_test.go (100%) create mode 100644 gocode/src/struct/issue847/issue847_test.go create mode 100644 gocode/src/struct/issue848/issue848_test.go create mode 100644 gocode/src/struct/issue849/issue849_test.go create mode 100644 gocode/src/struct/mktdata/fixme_test.go rename gocode/src/{ => struct}/mktdata/preallocated_test.go (100%) rename gocode/src/{ => struct}/simple/simple_test.go (100%) rename gocode/src/{ => struct}/since-deprecated/SinceDeprecated_test.go (100%) rename gocode/src/{ => struct}/vardata/SBE tests/TestMessage1_test.go (100%) create mode 100644 sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java create mode 100644 sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightOutputManager.java create mode 100644 sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightUtil.java rename sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/{ => struct}/GolangGenerator.java (99%) rename sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/{ => struct}/GolangOutputManager.java (98%) rename sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/{ => struct}/GolangUtil.java (98%) diff --git a/.gitignore b/.gitignore index 4ffe2537bc..9c76e676a6 100644 --- a/.gitignore +++ b/.gitignore @@ -65,31 +65,56 @@ cppbuild/Win32 # golang gocode/pkg -gocode/src/baseline/*.go -!gocode/src/baseline/*_test.go -gocode/src/baseline-bigendian/*.go -!gocode/src/baseline-bigendian/*_test.go -gocode/src/composite/*.go -!gocode/src/composite/*_test.go -gocode/src/composite_elements/*.go -!gocode/src/composite_elements/*_test.go -gocode/src/since-deprecated/*.go -!gocode/src/since-deprecated/*_test.go -gocode/src/group_with_data*/*.go -!gocode/src/group_with_data*/*_test.go -gocode/src/mktdata/*.go -!gocode/src/mktdata/*_test.go -gocode/src/simple/*.go -!gocode/src/simple/*_test.go -gocode/src/issue*/*.go -!gocode/src/issue*/*_test.go -gocode/src/*/*/*.go -!gocode/src/*/*/*_test.go -gocode/src/example-schema/example-schema* -gocode/src/example-schema/cpu.out -gocode/src/example-socket-clientserver/example-socket-clientserver -gocode/src/extension -gocode/src/extension2 +gocode/src/struct/baseline/*.go +!gocode/src/struct/baseline/*_test.go +gocode/src/struct/baseline-bigendian/*.go +!gocode/src/struct/baseline-bigendian/*_test.go +gocode/src/struct/composite/*.go +!gocode/src/struct/composite/*_test.go +gocode/src/struct/composite_elements/*.go +!gocode/src/struct/composite_elements/*_test.go +gocode/src/struct/since-deprecated/*.go +!gocode/src/struct/since-deprecated/*_test.go +gocode/src/struct/group_with_data*/*.go +!gocode/src/struct/group_with_data*/*_test.go +gocode/src/struct/mktdata/*.go +!gocode/src/struct/mktdata/*_test.go +gocode/src/struct/simple/*.go +!gocode/src/struct/simple/*_test.go +gocode/src/struct/issue*/*.go +!gocode/src/struct/issue*/*_test.go +gocode/src/struct/*/*/*.go +!gocode/src/struct/*/*/*_test.go +gocode/src/struct/example-schema/example-schema* +gocode/src/struct/example-schema/cpu.out +gocode/src/struct/example-socket-clientserver/example-socket-clientserver +gocode/src/struct/extension +gocode/src/struct/extension2 +gocode/src/flyweight/baseline/*.go +!gocode/src/flyweight/baseline/*_test.go +gocode/src/flyweight/baseline-bigendian/*.go +!gocode/src/flyweight/baseline-bigendian/*_test.go +gocode/src/flyweight/composite/*.go +!gocode/src/flyweight/composite/*_test.go +gocode/src/flyweight/composite_elements/*.go +!gocode/src/flyweight/composite_elements/*_test.go +gocode/src/flyweight/since-deprecated/*.go +!gocode/src/flyweight/since-deprecated/*_test.go +gocode/src/flyweight/group_with_data*/*.go +!gocode/src/flyweight/group_with_data*/*_test.go +gocode/src/flyweight/mktdata/*.go +!gocode/src/flyweight/mktdata/*_test.go +gocode/src/flyweight/simple/*.go +!gocode/src/flyweight/simple/*_test.go +gocode/src/flyweight/issue*/*.go +!gocode/src/flyweight/issue*/*_test.go +gocode/src/flyweight/*/*/*.go +!gocode/src/flyweight/*/*/*_test.go +gocode/src/flyweight/example-schema/example-schema* +gocode/src/flyweight/example-schema/cpu.out +gocode/src/flyweight/example-socket-clientserver/example-socket-clientserver +gocode/src/flyweight/extension +gocode/src/flyweight/extension2 # csharp csharp/*/bin diff --git a/README.md b/README.md index c393d2dd26..26efe26f10 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,8 @@ For convenience on Linux, a gnu Makefile is provided that runs some tests and co $ cd gocode # make # test, examples, bench +Go supports both generating Go structs with encode / decode methods, and flyweights like the other languages. Structs are generated by default for compatibility. Set `sbe.go.generate.generate.flyweights=true` to generate flyweights. + Users of golang generated code should see the [user documentation](https://github.com/real-logic/simple-binary-encoding/wiki/Golang-User-Guide). diff --git a/build.gradle b/build.gradle index 454e2f1e0f..43a6d93537 100644 --- a/build.gradle +++ b/build.gradle @@ -699,7 +699,7 @@ tasks.register('generateGolangCodecTestComposite', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src', + 'sbe.output.dir': 'gocode/src/struct', 'sbe.target.language': 'golang') args = ['sbe-tool/src/test/resources/composite-elements-schema-rc4.xml'] } @@ -708,7 +708,7 @@ tasks.register('generateGolangCodecTestBasic', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/basic', + 'sbe.output.dir': 'gocode/src/struct/basic', 'sbe.target.language': 'golang') args = ['sbe-tool/src/test/resources/basic-types-schema.xml'] } @@ -717,7 +717,7 @@ tasks.register('generateGolangCodecTestGroup', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/group', + 'sbe.output.dir': 'gocode/src/struct/group', 'sbe.target.language': 'golang') args = ['sbe-tool/src/test/resources/basic-group-schema.xml'] } @@ -726,7 +726,7 @@ tasks.register('generateGolangCodecTestVarData', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/vardata', + 'sbe.output.dir': 'gocode/src/struct/vardata', 'sbe.target.language': 'golang') args = ['sbe-tool/src/test/resources/basic-variable-length-schema.xml'] } @@ -735,7 +735,7 @@ tasks.register('generateGolangCodecsWithXIncludes', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src', + 'sbe.output.dir': 'gocode/src/struct', 'sbe.target.language': 'golang', 'sbe.xinclude.aware': 'true', 'sbe.validation.xsd': validationXsdPath) @@ -747,7 +747,87 @@ tasks.register('generateGolangCodecsWithXSD', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src', + 'sbe.output.dir': 'gocode/src/struct', + 'sbe.target.language': 'golang', + 'sbe.xinclude.aware': 'true', + 'sbe.validation.xsd': validationXsdPath) + args = ['sbe-tool/src/test/resources/group-with-data-schema.xml', + 'sbe-tool/src/test/resources/FixBinary.xml', + 'sbe-tool/src/test/resources/issue435.xml', + 'sbe-tool/src/test/resources/issue472.xml', + 'sbe-tool/src/test/resources/issue483.xml', + 'sbe-tool/src/test/resources/issue488.xml', + 'sbe-tool/src/test/resources/issue560.xml', + 'sbe-tool/src/test/resources/issue661.xml', + 'sbe-tool/src/test/resources/issue847.xml', + 'sbe-tool/src/test/resources/issue848.xml', + 'sbe-tool/src/test/resources/issue849.xml', + 'sbe-tool/src/test/resources/since-deprecated-test-schema.xml', + 'sbe-tool/src/test/resources/example-bigendian-test-schema.xml', + 'gocode/resources/example-composite.xml', + 'gocode/resources/group-with-data-extension-schema.xml', + 'gocode/resources/simple.xml'] +} + +tasks.register('generateGolangFlyweightCodecTestComposite', JavaExec) { + mainClass.set('uk.co.real_logic.sbe.SbeTool') + classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath + systemProperties( + 'sbe.output.dir': 'gocode/src/flyweight', + 'sbe.go.generate.generate.flyweights': 'true', + 'sbe.target.language': 'golang') + args = ['sbe-tool/src/test/resources/composite-elements-schema-rc4.xml'] +} + +tasks.register('generateGolangFlyweightCodecTestBasic', JavaExec) { + mainClass.set('uk.co.real_logic.sbe.SbeTool') + classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath + systemProperties( + 'sbe.output.dir': 'gocode/src/flyweight/basic', + 'sbe.go.generate.generate.flyweights': 'true', + 'sbe.target.language': 'golang') + args = ['sbe-tool/src/test/resources/basic-types-schema.xml'] +} + +tasks.register('generateGolangFlyweightCodecTestGroup', JavaExec) { + mainClass.set('uk.co.real_logic.sbe.SbeTool') + classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath + systemProperties( + 'sbe.output.dir': 'gocode/src/flyweight/group', + 'sbe.go.generate.generate.flyweights': 'true', + 'sbe.target.language': 'golang') + args = ['sbe-tool/src/test/resources/basic-group-schema.xml'] +} + +tasks.register('generateGolangFlyweightCodecTestVarData', JavaExec) { + mainClass.set('uk.co.real_logic.sbe.SbeTool') + classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath + systemProperties( + 'sbe.output.dir': 'gocode/src/flyweight/vardata', + 'sbe.go.generate.generate.flyweights': 'true', + 'sbe.target.language': 'golang') + args = ['sbe-tool/src/test/resources/basic-variable-length-schema.xml'] +} + +tasks.register('generateGolangFlyweightCodecsWithXIncludes', JavaExec) { + mainClass.set('uk.co.real_logic.sbe.SbeTool') + classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath + systemProperties( + 'sbe.output.dir': 'gocode/src/flyweight', + 'sbe.go.generate.generate.flyweights': 'true', + 'sbe.target.language': 'golang', + 'sbe.xinclude.aware': 'true', + 'sbe.validation.xsd': validationXsdPath) + args = ['sbe-samples/src/main/resources/example-schema.xml', + 'sbe-samples/src/main/resources/example-extension-schema.xml'] +} + +tasks.register('generateGolangFlyweightCodecsWithXSD', JavaExec) { + mainClass.set('uk.co.real_logic.sbe.SbeTool') + classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath + systemProperties( + 'sbe.output.dir': 'gocode/src/flyweight', + 'sbe.go.generate.generate.flyweights': 'true', 'sbe.target.language': 'golang', 'sbe.xinclude.aware': 'true', 'sbe.validation.xsd': validationXsdPath) @@ -779,6 +859,12 @@ tasks.register('generateGolangCodecs') { 'generateGolangCodecTestComposite', 'generateGolangCodecsWithXIncludes', 'generateGolangCodecsWithXSD', + 'generateGolangFlyweightCodecTestVarData', + 'generateGolangFlyweightCodecTestGroup', + 'generateGolangFlyweightCodecTestBasic', + 'generateGolangFlyweightCodecTestComposite', + 'generateGolangFlyweightCodecsWithXIncludes', + 'generateGolangFlyweightCodecsWithXSD', ':sbe-all:jar' } diff --git a/gocode/Makefile b/gocode/Makefile index eebb2b4f38..0acd432c73 100644 --- a/gocode/Makefile +++ b/gocode/Makefile @@ -19,11 +19,11 @@ all: example test # bench # This target is used to build golang files using parent gradle # script's invocation of sbe-tool in case it needs to be run again. We # use this one output file to check that dependency as it's simple -DEP=src/simple/SbeMarshalling.go +DEP=src/struct/simple/SbeMarshalling.go $(DEP): $(SBE_JAR) (cd ..; ./gradlew generateGolangCodecs) (export GOPATH=$(GOPATH) && \ - cd src/simple && \ + cd src/struct/simple && \ go build && \ $(SAVE_FORMAT) \ go fmt && \ @@ -31,27 +31,27 @@ $(DEP): $(SBE_JAR) # Will regenerate codecs by removing dependencies clean: - rm -f src/*/SbeMarshalling.go src/*/*/SbeMarshalling.go + rm -f src/struct/*/SbeMarshalling.go src/struct/*/*/SbeMarshalling.go # Example code and benchmarking -example: src/example-schema/example-schema -src/example-schema/example-schema: $(DEP) +example: src/struct/example-schema/example-schema +src/struct/example-schema/example-schema: $(DEP) (export GOPATH=$(GOPATH) && \ - cd src/example-schema && \ + cd src/struct/example-schema && \ go build && \ go fmt && \ ./example-schema) bench: $(DEP) (export GOPATH=$(GOPATH) && \ - cd src/example-schema && \ + cd src/struct/example-schema && \ go test --bench . -cpuprofile=cpu.out && \ go tool pprof -text example-schema.test cpu.out) -src/baseline/SbeMarshalling.go: $(DEP) +src/struct/baseline/SbeMarshalling.go: $(DEP) $(GOPATH)/sbe-tool -d src -s $(EXAMPLES_SCHEMA) (export GOPATH=$(GOPATH) && \ - cd src/baseline && \ + cd src/struct/baseline && \ go build && \ go fmt && \ go test && \ @@ -59,13 +59,13 @@ src/baseline/SbeMarshalling.go: $(DEP) # The first set does a make install as there is a test that uses # multiple packages and needs them in GOPATH The second set work in -# src/foo, and the third need a GOPATH addition as for Java and C++ +# src/struct/foo, and the third need a GOPATH addition as for Java and C++ # they generate into the same directory but golang doesn't allow that test: $(DEP) (export GOPATH=$(GOPATH) && \ (for t in baseline extension; do \ export GOPATH=$(GOPATH) && \ - cd $(GOPATH)/src/$$t && \ + cd $(GOPATH)/src/struct/$$t && \ go build && \ go fmt && \ go test && \ @@ -73,14 +73,37 @@ test: $(DEP) ;done)) (export GOPATH=$(GOPATH) && \ (for t in baseline-bigendian mktdata group_with_data group_with_data_extension composite_elements composite since-deprecated simple issue435 issue472 issue483 issue488 issue560 issue847 issue848 issue849 issue505 test973; do \ - cd $(GOPATH)/src/$$t && \ + cd $(GOPATH)/src/struct/$$t && \ go build && \ go fmt && \ go test \ ;done)) (for t in vardata group basic; do \ export GOPATH=$(GOPATH)/$$t && \ - cd $(GOPATH)/src/$$t/'SBE tests' && \ + cd $(GOPATH)/src/struct/$$t/'SBE tests' && \ + go build && \ + go fmt && \ + go test \ + ;done) + (export GOPATH=$(GOPATH) && \ + (for t in baseline extension; do \ + export GOPATH=$(GOPATH) && \ + cd $(GOPATH)/src/flyweight/$$t && \ + go build && \ + go fmt && \ + go test && \ + go install \ + ;done)) + (export GOPATH=$(GOPATH) && \ + (for t in baseline-bigendian mktdata group_with_data group_with_data_extension composite_elements composite since-deprecated simple issue435 issue472 issue483 issue488 issue560 issue847 issue848 issue849; do \ + cd $(GOPATH)/src/flyweight/$$t && \ + go build && \ + go fmt && \ + go test \ + ;done)) + (for t in vardata group basic; do \ + export GOPATH=$(GOPATH)/$$t && \ + cd $(GOPATH)/src/flyweight/$$t/'SBE tests' && \ go build && \ go fmt && \ go test \ diff --git a/gocode/README.md b/gocode/README.md index fb8b994539..5740d9dbca 100755 --- a/gocode/README.md +++ b/gocode/README.md @@ -5,10 +5,13 @@ There is now a [user guide](https://github.com/real-logic/simple-binary-encoding/wiki/Golang-User-Guide) and this document is for development of the SBE golang generator. +Go supports both generating Go structs with encode / decode methods, and flyweights like the other languages. +Structs are generated by default for compatibility. Set `sbe.go.generate.generate.flyweights=true` to generate flyweights. + Code Layout ----------- The Java code that performs the generation of golang code is -[here](https://github.com/real-logic/simple-binary-encoding/tree/master/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang). +[here](https://github.com/real-logic/simple-binary-encoding/tree/master/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang). There is both a struct and a flyweight generator. Golang code used for testing resides in the top-level [gocode directory](https://github.com/real-logic/simple-binary-encoding/tree/master/gocode). @@ -24,11 +27,12 @@ the build environment, example and test code are structured into a top level gocode directory hierarchy. Code is generated into this structure with pre-existing test code in place. +There are tests for both the struct and flyweight styles of generated code. For the example, the code is generated into a library and the matching example code lives in it's own directory at the same level. For example, the example-schema generates the baseline library code into -`gocode/src/baseline` and example code lives in `gocode/src/example-schema`. +`gocode/src/struct/baseline` and example code lives in `gocode/src/example-schema`. To use this layout you should `set GOPATH=/path/to/gocode` or use the supplied Makefile which does this for you. For the tests you will need @@ -60,5 +64,3 @@ generator processes. Roadmap ======= * Windows developer support (currently tested on Linux/MacOS) - * Further Unicode support - * Testing/Bug fixes diff --git a/gocode/go.mod b/gocode/go.mod new file mode 100644 index 0000000000..af460264e6 --- /dev/null +++ b/gocode/go.mod @@ -0,0 +1,7 @@ +module github.com/real-logic/simple-binary-encoding + +go 1.12 + +require ( + github.com/stretchr/testify v1.7.0 +) diff --git a/gocode/go.sum b/gocode/go.sum new file mode 100644 index 0000000000..af7675440c --- /dev/null +++ b/gocode/go.sum @@ -0,0 +1,7 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/gocode/src/flyweight/baseline-bigendian/Car_test.go b/gocode/src/flyweight/baseline-bigendian/Car_test.go new file mode 100644 index 0000000000..d06ed4102a --- /dev/null +++ b/gocode/src/flyweight/baseline-bigendian/Car_test.go @@ -0,0 +1,146 @@ +package baseline_bigendian + +import ( + _ "fmt" + "testing" +) + +func TestEncodeDecodeCar(t *testing.T) { + var data [256]byte + var in Car + in.WrapForEncode(data[:], 0, uint64(len(data))) + + in.SetSerialNumber(1234). + SetModelYear(2000). + SetAvailable(BooleanType_T). + SetCode(Model_A). + SetVehicleCode("abcdef") + + in.SetSomeNumbersIndex(0, 0) + in.SetSomeNumbersIndex(1, 1) + in.SetSomeNumbersIndex(2, 2) + in.SetSomeNumbersIndex(3, 3) + in.SetSomeNumbersIndex(4, 4) + + in.Extras(). + SetCruiseControl(true). + SetSportsPack(true) + + in.Engine(). + SetCapacity(2000). + SetNumCylinders(4). + SetManufacturerCode("123"). + SetEfficiency(42). + SetBoosterEnabled(BooleanType_T). + Booster(). + SetBoostType(BoostType_NITROUS). + SetHorsePower(200) + + in.FuelFiguresCount(3).Next(). + SetSpeed(30). + SetMpg(35.9). + PutUsageDescription("Urban Cycle").Next(). + SetSpeed(55). + SetMpg(49.0). + PutUsageDescription("Combined Cycle").Next(). + SetSpeed(75). + SetMpg(40.0). + PutUsageDescription("Highway Cycle") + + performanceFigures := in.PerformanceFiguresCount(2) + performanceFigures.SetOctaneRating(95).Next(). + AccelerationCount(3). + SetMph(30). + SetSeconds(3.8).Next(). + SetMph(60). + SetSeconds(7.5).Next(). + SetMph(100). + SetSeconds(12.2) + performanceFigures.Next(). + SetOctaneRating(99). + AccelerationCount(3). + SetMph(30). + SetSeconds(3.8).Next(). + SetMph(60). + SetSeconds(7.5).Next(). + SetMph(100). + SetSeconds(12.2) + + in.PutManufacturer("123"). + PutModel("Civic VTi"). + PutActivationCode("deadbeef") + + var out Car + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeSchemaVersion()), + uint64(in.SbeBlockLength()), + uint64(len(data)), + ) + + if in.SerialNumber() != out.SerialNumber() { + t.Log("in.SerialNumber != out.SerialNumber:\n", in.SerialNumber(), out.SerialNumber()) + t.Fail() + } + if in.ModelYear() != out.ModelYear() { + t.Log("in.ModelYear != out.ModelYear:\n", in.ModelYear(), out.ModelYear()) + t.Fail() + } + if in.Available() != out.Available() { + t.Log("in.Available != out.Available:\n", in.Available(), out.Available()) + t.Fail() + } + if in.Code() != out.Code() { + t.Log("in.Code != out.Code:\n", in.Code(), out.Code()) + t.Fail() + } + for i, l := uint64(0), uint64(in.SomeNumbersLength()); i < l; i++ { + if in.SomeNumbersIndex(i) != out.SomeNumbersIndex(i) { + t.Log("in.SomeNumbers != out.SomeNumbers:\n", in.SomeNumbersIndex(i), out.SomeNumbersIndex(i)) + t.Fail() + } + } + for i, l := uint64(0), uint64(in.VehicleCodeLength()); i < l; i++ { + if in.VehicleCodeIndex(i) != out.VehicleCodeIndex(i) { + t.Log("in.VehicleCode != out.VehicleCode:\n", in.VehicleCodeIndex(i), out.VehicleCodeIndex(i)) + t.Fail() + } + } + if in.Extras().String() != out.Extras().String() { + t.Log("in.Extras != out.Extras:\n", in.Extras(), out.Extras()) + t.Fail() + } + + // DiscountedModel is constant + if Model_C != out.DiscountedModel() { + t.Log("in.DiscountedModel != out.DiscountedModel:\n", in.DiscountedModel(), out.DiscountedModel()) + t.Fail() + } + + if in.Engine().String() != out.Engine().String() { + t.Log("in.Engine != out.Engine:\n", in.Engine().String(), out.Engine().String()) + t.Fail() + } + + return + +} + +func TestDecodeJavaBuffer(t *testing.T) { + // The byte array is from the java example for interop test + // made by editing example-schgema to be bigendian and running + // with the example with -Dsbe.encoding.filename + // and then decoded using od -tu1 + data := []byte{0, 49, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 4, 210, 7, 221, 1, 65, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 97, 98, 99, 100, 101, 102, 6, 7, 208, 4, 49, 50, 51, 35, 1, 78, 200, 0, 6, 0, 3, 0, 30, 66, 15, 153, 154, 0, 0, 0, 11, 85, 114, 98, 97, 110, 32, 67, 121, 99, 108, 101, 0, 55, 66, 68, 0, 0, 0, 0, 0, 14, 67, 111, 109, 98, 105, 110, 101, 100, 32, 67, 121, 99, 108, 101, 0, 75, 66, 32, 0, 0, 0, 0, 0, 13, 72, 105, 103, 104, 119, 97, 121, 32, 67, 121, 99, 108, 101, 0, 1, 0, 2, 95, 0, 6, 0, 3, 0, 30, 64, 128, 0, 0, 0, 60, 64, 240, 0, 0, 0, 100, 65, 67, 51, 51, 99, 0, 6, 0, 3, 0, 30, 64, 115, 51, 51, 0, 60, 64, 227, 51, 51, 0, 100, 65, 60, 204, 205, 0, 0, 0, 5, 72, 111, 110, 100, 97, 0, 0, 0, 9, 67, 105, 118, 105, 99, 32, 86, 84, 105, 0, 0, 0, 6, 97, 98, 99, 100, 101, 102} + + var c Car + c.WrapAndApplyHeader(data, 0, uint64(len(data))) + + expected := `{"Name": "Car", "sbeTemplateId": 1, "serialNumber": "1234", "modelYear": "2013", "available": "T", "code": "A", "someNumbers": [0,1,2,3,4], "vehicleCode": "abcdef", "extras": ["SportsPack","CruiseControl"], "discountedModel": "C", "engine": {"capacity": "2000", "numCylinders": "4", "manufacturerCode": "123", "efficiency": "35", "boosterEnabled": "T", "booster": {"BoostType": "NITROUS", "horsePower": "200"}}, "fuelFigures": [{"speed": "30", "mpg": "35.9", "usageDescription": "Urban Cycle"}, {"speed": "55", "mpg": "49", "usageDescription": "Combined Cycle"}, {"speed": "75", "mpg": "40", "usageDescription": "Highway Cycle"}], "performanceFigures": [{"octaneRating": "95", "acceleration": [{"mph": "30", "seconds": "4"}, {"mph": "60", "seconds": "7.5"}, {"mph": "100", "seconds": "12.2"}]}, {"octaneRating": "99", "acceleration": [{"mph": "30", "seconds": "3.8"}, {"mph": "60", "seconds": "7.1"}, {"mph": "100", "seconds": "11.8"}]}], "manufacturer": "Honda", "model": "Civic VTi", "activationCode": "abcdef"}` + if actual := c.String(); actual != expected { + t.Logf("Failed to decode car, expected %s, got %s", expected, actual) + t.Fail() + } + return +} diff --git a/gocode/src/flyweight/baseline/Car_test.go b/gocode/src/flyweight/baseline/Car_test.go new file mode 100644 index 0000000000..9aa2127b7a --- /dev/null +++ b/gocode/src/flyweight/baseline/Car_test.go @@ -0,0 +1,138 @@ +package baseline + +import ( + _ "fmt" + "testing" +) + +func TestEncodeDecodeCar(t *testing.T) { + var data [256]byte + var in Car + in.WrapForEncode(data[:], 0, uint64(len(data))) + + in.SetSerialNumber(1234). + SetModelYear(2000). + SetAvailable(BooleanType_T). + SetCode(Model_A). + PutSomeNumbersValues(0, 1, 2, 3). + SetVehicleCode("abcdef") + + in.Extras(). + SetCruiseControl(true). + SetSportsPack(true) + + in.Engine(). + SetCapacity(2000). + SetNumCylinders(4). + SetManufacturerCode("123"). + SetEfficiency(42). + SetBoosterEnabled(BooleanType_T). + Booster(). + SetBoostType(BoostType_NITROUS). + SetHorsePower(200) + + in.FuelFiguresCount(3).Next(). + SetSpeed(30). + SetMpg(35.9). + PutUsageDescription("Urban Cycle").Next(). + SetSpeed(55). + SetMpg(49.0). + PutUsageDescription("Combined Cycle").Next(). + SetSpeed(75). + SetMpg(40.0). + PutUsageDescription("Highway Cycle") + + performanceFigures := in.PerformanceFiguresCount(2) + performanceFigures.SetOctaneRating(95).Next(). + AccelerationCount(3). + SetMph(30). + SetSeconds(3.8).Next(). + SetMph(60). + SetSeconds(7.5).Next(). + SetMph(100). + SetSeconds(12.2) + performanceFigures.Next(). + SetOctaneRating(99). + AccelerationCount(3). + SetMph(30). + SetSeconds(3.8).Next(). + SetMph(60). + SetSeconds(7.5).Next(). + SetMph(100). + SetSeconds(12.2) + + in.PutManufacturer("123"). + PutModel("Civic VTi"). + PutActivationCode("deadbeef") + + var out Car + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeSchemaVersion()), + uint64(in.SbeBlockLength()), + uint64(len(data)), + ) + + if in.SerialNumber() != out.SerialNumber() { + t.Log("in.SerialNumber != out.SerialNumber:\n", in.SerialNumber(), out.SerialNumber()) + t.Fail() + } + if in.ModelYear() != out.ModelYear() { + t.Log("in.ModelYear != out.ModelYear:\n", in.ModelYear(), out.ModelYear()) + t.Fail() + } + if in.Available() != out.Available() { + t.Log("in.Available != out.Available:\n", in.Available(), out.Available()) + t.Fail() + } + if in.Code() != out.Code() { + t.Log("in.Code != out.Code:\n", in.Code(), out.Code()) + t.Fail() + } + for i, l := uint64(0), uint64(in.SomeNumbersLength()); i < l; i++ { + if in.SomeNumbersIndex(i) != out.SomeNumbersIndex(i) { + t.Log("in.SomeNumbers != out.SomeNumbers:\n", in.SomeNumbersIndex(i), out.SomeNumbersIndex(i)) + t.Fail() + } + } + for i, l := uint64(0), uint64(in.VehicleCodeLength()); i < l; i++ { + if in.VehicleCodeIndex(i) != out.VehicleCodeIndex(i) { + t.Log("in.VehicleCode != out.VehicleCode:\n", in.VehicleCodeIndex(i), out.VehicleCodeIndex(i)) + t.Fail() + } + } + if in.Extras().String() != out.Extras().String() { + t.Log("in.Extras != out.Extras:\n", in.Extras(), out.Extras()) + t.Fail() + } + + // DiscountedModel is constant + if Model_C != out.DiscountedModel() { + t.Log("in.DiscountedModel != out.DiscountedModel:\n", in.DiscountedModel(), out.DiscountedModel()) + t.Fail() + } + + if in.Engine().String() != out.Engine().String() { + t.Log("in.Engine != out.Engine:\n", in.Engine().String(), out.Engine().String()) + t.Fail() + } + + return + +} + +func TestDecodeJavaBuffer(t *testing.T) { + // See ~gocode/src/example-schema/CarExample.go for how this is generated + data := []byte{45, 0, 1, 0, 1, 0, 0, 0, 210, 4, 0, 0, 0, 0, 0, 0, 221, 7, 1, 65, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 97, 98, 99, 100, 101, 102, 6, 208, 7, 4, 49, 50, 51, 35, 1, 78, 200, 6, 0, 3, 0, 30, 0, 154, 153, 15, 66, 11, 0, 0, 0, 85, 114, 98, 97, 110, 32, 67, 121, 99, 108, 101, 55, 0, 0, 0, 68, 66, 14, 0, 0, 0, 67, 111, 109, 98, 105, 110, 101, 100, 32, 67, 121, 99, 108, 101, 75, 0, 0, 0, 32, 66, 13, 0, 0, 0, 72, 105, 103, 104, 119, 97, 121, 32, 67, 121, 99, 108, 101, 1, 0, 2, 0, 95, 6, 0, 3, 0, 30, 0, 0, 0, 128, 64, 60, 0, 0, 0, 240, 64, 100, 0, 51, 51, 67, 65, 99, 6, 0, 3, 0, 30, 0, 51, 51, 115, 64, 60, 0, 51, 51, 227, 64, 100, 0, 205, 204, 60, 65, 5, 0, 0, 0, 72, 111, 110, 100, 97, 9, 0, 0, 0, 67, 105, 118, 105, 99, 32, 86, 84, 105, 6, 0, 0, 0, 97, 98, 99, 100, 101, 102} + + var c Car + c.WrapAndApplyHeader(data, 0, uint64(len(data))) + + expected := `{"Name": "Car", "sbeTemplateId": 1, "serialNumber": "1234", "modelYear": "2013", "available": "T", "code": "A", "someNumbers": [1,2,3,4], "vehicleCode": "abcdef", "extras": ["SportsPack","CruiseControl"], "discountedModel": "C", "engine": {"capacity": "2000", "numCylinders": "4", "manufacturerCode": "123", "efficiency": "35", "boosterEnabled": "T", "booster": {"BoostType": "NITROUS", "horsePower": "200"}}, "fuelFigures": [{"speed": "30", "mpg": "35.9", "usageDescription": "Urban Cycle"}, {"speed": "55", "mpg": "49", "usageDescription": "Combined Cycle"}, {"speed": "75", "mpg": "40", "usageDescription": "Highway Cycle"}], "performanceFigures": [{"octaneRating": "95", "acceleration": [{"mph": "30", "seconds": "4"}, {"mph": "60", "seconds": "7.5"}, {"mph": "100", "seconds": "12.2"}]}, {"octaneRating": "99", "acceleration": [{"mph": "30", "seconds": "3.8"}, {"mph": "60", "seconds": "7.1"}, {"mph": "100", "seconds": "11.8"}]}], "manufacturer": "Honda", "model": "Civic VTi", "activationCode": "abcdef"}` + if actual := c.String(); actual != expected { + t.Logf("Failed to decode car, expected %s, got %s", expected, actual) + t.Fail() + } + return +} diff --git a/gocode/src/flyweight/basic/SBE tests/ENUM_test.go b/gocode/src/flyweight/basic/SBE tests/ENUM_test.go new file mode 100644 index 0000000000..0a3418592d --- /dev/null +++ b/gocode/src/flyweight/basic/SBE tests/ENUM_test.go @@ -0,0 +1,12 @@ +package sbe_tests + +import ( + "testing" +) + +func TestEnum(t *testing.T) { + e := ENUM_Value1 + if e.String() != "Value1" { + t.Fail() + } +} diff --git a/gocode/src/flyweight/basic/SBE tests/Message1_test.go b/gocode/src/flyweight/basic/SBE tests/Message1_test.go new file mode 100644 index 0000000000..c6963115a2 --- /dev/null +++ b/gocode/src/flyweight/basic/SBE tests/Message1_test.go @@ -0,0 +1,29 @@ +package sbe_tests + +import ( + "testing" +) + +func TestEncodeDecodeMessage1(t *testing.T) { + var data [256]byte + var in Message1 + in.WrapForEncode(data[:], 0, uint64(len(data))) + + in.SetEDTField("abcdefghijklmnopqrst") + in.SetENUMField(ENUM_Value10) + + var out Message1 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + expected := `{"Name": "Message1", "sbeTemplateId": 1, "header": {"blockLength": "0", "templateId": "0", "schemaId": "0", "version": "0"}, "EDTField": "abcdefghijklmnopqrst", "ENUMField": "Value10", "SETField": [], "int64Field": "0"}` + if actual := out.String(); actual != expected { + t.Logf("Failed to decode, expected %s, got %s", expected, actual) + t.Fail() + } +} diff --git a/gocode/src/flyweight/basic/SBE tests/SET_test.go b/gocode/src/flyweight/basic/SBE tests/SET_test.go new file mode 100644 index 0000000000..936730c617 --- /dev/null +++ b/gocode/src/flyweight/basic/SBE tests/SET_test.go @@ -0,0 +1,28 @@ +package sbe_tests + +import ( + "testing" +) + +func TestEncodeDecodeSet(t *testing.T) { + var data [256]byte + var in SET + in.Wrap(data[:], 0, 0, uint64(len(data))) + in.SetBit16(true) + + var out SET + out.Wrap( + data[:], + 0, + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + if out.Bit0() { + t.Fail() + } + + if !out.Bit16() { + t.Fail() + } +} diff --git a/gocode/src/flyweight/composite/Composite_test.go b/gocode/src/flyweight/composite/Composite_test.go new file mode 100644 index 0000000000..11f5f8ed83 --- /dev/null +++ b/gocode/src/flyweight/composite/Composite_test.go @@ -0,0 +1,44 @@ +package composite + +import ( + "testing" +) + +func TestEncodeDecode(t *testing.T) { + var data [256]byte + var in Composite + in.WrapForEncode(data[:], 0, uint64(len(data))) + + in.Start(). + SetName("start"). + SetD(3.14). + SetI(1). + SetUIndex(0, 66). + SetUIndex(1, 77). + SetTruthval1(BooleanEnum_NULL_VALUE). + SetTruthval2(BooleanEnum_T) + + in.End(). + SetName("end"). + SetD(0.31). + SetI(2). + SetUIndex(0, 77). + SetUIndex(1, 88). + SetTruthval1(BooleanEnum_T). + SetTruthval2(BooleanEnum_F) + + var out Composite + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeSchemaVersion()), + uint64(in.SbeBlockLength()), + uint64(len(data)), + ) + + expected := `{"Name": "Composite", "sbeTemplateId": 1, "start": {"name": "start", "d": "3.14", "i": "1", "u": [66,77], "truthval1": "NULL_VALUE", "truthval2": "T"}, "end": {"name": "end", "d": "0.31", "i": "2", "u": [77,88], "truthval1": "T", "truthval2": "F"}}` + if actual := out.String(); actual != expected { + t.Logf("Failed to decode, expected %s, got %s", expected, actual) + t.Fail() + } +} diff --git a/gocode/src/flyweight/composite_elements/Message_test.go b/gocode/src/flyweight/composite_elements/Message_test.go new file mode 100644 index 0000000000..90dfe20f98 --- /dev/null +++ b/gocode/src/flyweight/composite_elements/Message_test.go @@ -0,0 +1,91 @@ +package composite_elements + +import ( + "testing" +) + +func TestEncodeDecodeMsg(t *testing.T) { + var data [256]byte + var in Msg + in.WrapForEncode(data[:], 0, uint64(len(data))) + + in.Structure(). + SetEnumOne(EnumOne_Value10). + SetZeroth(0) + in.Structure(). + SetOne().SetBit0(true) + in.Structure().Inner(). + SetFirst(1). + SetSecond(2) + + var out Msg + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeSchemaVersion()), + uint64(in.SbeBlockLength()), + uint64(len(data)), + ) + + expected := `{"Name": "Msg", "sbeTemplateId": 1, "structure": {"enumOne": "Value10", "zeroth": "0", "setOne": ["Bit0"], "inner": {"first": "1", "second": "2"}}}` + if actual := out.String(); actual != expected { + t.Logf("Failed to decode, expected %s, got %s", expected, actual) + t.Fail() + } +} + +func TestEncodeDecodeMsg2(t *testing.T) { + var data [256]byte + var in Msg2 + in.WrapForEncode(data[:], 0, uint64(len(data))) + + in.Structure(). + SetEnumOne(EnumOne_Value10). + SetZeroth(0) + in.Structure(). + SetOne().SetBit16(true) + in.Structure().Inner(). + SetFirst(1). + SetSecond(2) + + var out Msg2 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeSchemaVersion()), + uint64(in.SbeBlockLength()), + uint64(len(data)), + ) + + expected := `{"Name": "Msg2", "sbeTemplateId": 2, "structure": {"enumOne": "Value10", "zeroth": "0", "setOne": ["Bit16"], "inner": {"first": "1", "second": "2"}}}` + if actual := out.String(); actual != expected { + t.Logf("Failed to decode, expected %s, got %s", expected, actual) + t.Fail() + } +} + +func TestEncodeDecodeMsg3(t *testing.T) { + var data [256]byte + var in Msg3 + in.WrapForEncode(data[:], 0, uint64(len(data))) + + in.Structure(). + SetMantissa(65). + SetExponent(3). + SetIsSettlement(BooleanEnum_T) + + var out Msg3 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeSchemaVersion()), + uint64(in.SbeBlockLength()), + uint64(len(data)), + ) + + expected := `{"Name": "Msg3", "sbeTemplateId": 3, "structure": {"mantissa": "65", "exponent": "3", "isSettlement": "T"}}` + if actual := out.String(); actual != expected { + t.Logf("Failed to decode, expected %s, got %s", expected, actual) + t.Fail() + } +} diff --git a/gocode/src/flyweight/example-schema/CarExample_test.go b/gocode/src/flyweight/example-schema/CarExample_test.go new file mode 100644 index 0000000000..12b8d3edc8 --- /dev/null +++ b/gocode/src/flyweight/example-schema/CarExample_test.go @@ -0,0 +1,572 @@ +package main + +import ( + "fmt" + "github.com/real-logic/simple-binary-encoding/src/baseline" + "github.com/real-logic/simple-binary-encoding/src/extension" + "io" + "testing" +) + +func TestNoop(t *testing.T) { +} + +func BenchmarkInstantiateCar(b *testing.B) { + var data [256]byte + for i := 0; i < b.N; i++ { + var hdr baseline.MessageHeader + var car baseline.Car + makeCar(&hdr, &car, data[:], 0) + } +} + +func BenchmarkDecodeStrict(b *testing.B) { + var data [256]byte + var hdr baseline.MessageHeader + var car baseline.Car + makeCar(&hdr, &car, data[:], 0) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + var out baseline.Car + out.WrapForDecode( + data[:], + 0, + uint64(car.SbeBlockLength()), + uint64(car.SbeSchemaVersion()), + uint64(len(data)), + ) + } +} + +func BenchmarkPipe(b *testing.B) { + var r, w = io.Pipe() + data := []byte{49, 0, 1, 0, 1, 0, 0, 0, 210, 4, 0, 0, 0, 0, 0, 0, 221, 7, 1, 65, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 97, 98, 99, 100, 101, 102, 6, 208, 7, 4, 49, 50, 51, 35, 1, 78, 200, 6, 0, 3, 0, 30, 0, 154, 153, 15, 66, 11, 0, 0, 0, 85, 114, 98, 97, 110, 32, 67, 121, 99, 108, 101, 55, 0, 0, 0, 68, 66, 14, 0, 0, 0, 67, 111, 109, 98, 105, 110, 101, 100, 32, 67, 121, 99, 108, 101, 75, 0, 0, 0, 32, 66, 13, 0, 0, 0, 72, 105, 103, 104, 119, 97, 121, 32, 67, 121, 99, 108, 101, 1, 0, 2, 0, 95, 6, 0, 3, 0, 30, 0, 0, 0, 128, 64, 60, 0, 0, 0, 240, 64, 100, 0, 51, 51, 67, 65, 99, 6, 0, 3, 0, 30, 0, 51, 51, 115, 64, 60, 0, 51, 51, 227, 64, 100, 0, 205, 204, 60, 65, 5, 0, 0, 0, 72, 111, 110, 100, 97, 9, 0, 0, 0, 67, 105, 118, 105, 99, 32, 86, 84, 105, 6, 0, 0, 0, 97, 98, 99, 100, 101, 102} + writerReady := make(chan bool) + + go func() { + defer w.Close() + writerReady <- true + // By way of test, stream the bytes into the pipe a + // chunk at a time + for looping := true; looping; { + if _, err := w.Write(data); err != nil { + looping = false + } + } + }() + + <-writerReady + b.ResetTimer() + + var buf [1024]byte + for i := 0; i < b.N; i++ { + var hdr baseline.MessageHeader + hdrSize := int(hdr.EncodedLength()) + _, err := io.ReadAtLeast(r, buf[:], hdrSize) + if err != nil { + b.Log("Failed to read", err) + b.Fail() + } + hdr.Wrap(buf[:], 0, 0, uint64(hdrSize)) + + n, err := io.ReadAtLeast(r, buf[:], hdrSize) + if err != nil { + b.Log("Failed to read", err) + b.Fail() + } + + var out baseline.Car + out.WrapForDecode( + buf[hdrSize:], + 0, + uint64(hdr.BlockLength()), + hdr.ActingVersion(), + uint64(n-hdrSize), + ) + } + r.Close() +} + +func BenchmarkPipeBufio(b *testing.B) { + var r, w = io.Pipe() + data := []byte{49, 0, 1, 0, 1, 0, 0, 0, 210, 4, 0, 0, 0, 0, 0, 0, 221, 7, 1, 65, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 97, 98, 99, 100, 101, 102, 6, 208, 7, 4, 49, 50, 51, 35, 1, 78, 200, 6, 0, 3, 0, 30, 0, 154, 153, 15, 66, 11, 0, 0, 0, 85, 114, 98, 97, 110, 32, 67, 121, 99, 108, 101, 55, 0, 0, 0, 68, 66, 14, 0, 0, 0, 67, 111, 109, 98, 105, 110, 101, 100, 32, 67, 121, 99, 108, 101, 75, 0, 0, 0, 32, 66, 13, 0, 0, 0, 72, 105, 103, 104, 119, 97, 121, 32, 67, 121, 99, 108, 101, 1, 0, 2, 0, 95, 6, 0, 3, 0, 30, 0, 0, 0, 128, 64, 60, 0, 0, 0, 240, 64, 100, 0, 51, 51, 67, 65, 99, 6, 0, 3, 0, 30, 0, 51, 51, 115, 64, 60, 0, 51, 51, 227, 64, 100, 0, 205, 204, 60, 65, 5, 0, 0, 0, 72, 111, 110, 100, 97, 9, 0, 0, 0, 67, 105, 118, 105, 99, 32, 86, 84, 105, 6, 0, 0, 0, 97, 98, 99, 100, 101, 102} + + writerReady := make(chan bool) + + go func() { + defer w.Close() + writerReady <- true + // By way of test, stream the bytes into the pipe a + // chunk at a time + for looping := true; looping; { + if _, err := w.Write(data); err != nil { + looping = false + } + } + }() + + <-writerReady + b.ResetTimer() + + var buf [1024]byte + for i := 0; i < b.N; i++ { + var hdr baseline.MessageHeader + hdrSize := int(hdr.EncodedLength()) + _, err := io.ReadAtLeast(r, buf[:], hdrSize) + if err != nil { + b.Log("Failed to read", err) + b.Fail() + } + hdr.Wrap(buf[:], 0, 0, uint64(hdrSize)) + + n, err := io.ReadAtLeast(r, buf[:], hdrSize) + if err != nil { + b.Log("Failed to read", err) + b.Fail() + } + + var out baseline.Car + out.WrapForDecode( + buf[:], + uint64(hdrSize), + uint64(hdr.BlockLength()), + hdr.ActingVersion(), + uint64(n-hdrSize), + ) + } + r.Close() +} + +// String Preallocations which matches what the Java and C++ benchmarks do +var vehicleCode = [6]byte{'a', 'b', 'c', 'd', 'e', 'f'} +var manufacturerCode = [3]byte{'1', '2', '3'} +var manufacturer = []uint8("Honda") +var model = []uint8("Civic VTi") +var activationCode = []uint8("abcdef") + +// Both Java and C++ benchmarks ignore CarFuelFigures.UsageDescription +var urban = []uint8("Urban Cycle") +var combined = []uint8("Combined Cycle") +var highway = []uint8("Highway Cycle") + +func ExampleEncodeDecode() { + var data [256]byte + var hdr baseline.MessageHeader + var in baseline.Car + + makeCar(&hdr, &in, data[:], 0) + in.WrapForDecode( + data[:], + hdr.EncodedLength(), + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data))-hdr.EncodedLength(), + ) + + var out baseline.Car + out.WrapForDecode( + data[:], + hdr.EncodedLength(), + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data))-hdr.EncodedLength(), + ) + + if in.SerialNumber() != out.SerialNumber() { + fmt.Println("in.SerialNumber() != out.SerialNumber():\n", in.SerialNumber(), out.SerialNumber()) + return + } + if in.ModelYear() != out.ModelYear() { + fmt.Println("in.ModelYear() != out.ModelYear():\n", in.ModelYear(), out.ModelYear()) + return + } + if in.Available() != out.Available() { + fmt.Println("in.Available() != out.Available():\n", in.Available(), out.Available()) + return + } + if in.Code() != out.Code() { + fmt.Println("in.Code() != out.Code():\n", in.Code(), out.Code()) + return + } + + if in.SomeNumbersLength() != out.SomeNumbersLength() { + fmt.Println("in.SomeNumbers != out.SomeNumbers:\n", in.SomeNumbersLength(), out.SomeNumbersLength()) + return + } + for i := uint64(0); i < uint64(in.SomeNumbersLength()); i++ { + if in.SomeNumbersIndex(i) != out.SomeNumbersIndex(i) { + fmt.Println("in.SomeNumbers != out.SomeNumbers:\n", in.SomeNumbersIndex(i), out.SomeNumbersIndex(i)) + return + } + } + + if in.GetVehicleCodeAsString() != out.GetVehicleCodeAsString() { + fmt.Println("in.VehicleCode != out.VehicleCode:\n", in.GetVehicleCodeAsString(), out.GetVehicleCodeAsString()) + return + } + if in.Extras().String() != out.Extras().String() { + fmt.Println("in.Extras != out.Extras:\n", in.Extras().String(), out.Extras().String()) + return + } + if baseline.Model_C != out.DiscountedModel() { + fmt.Println("in.DiscountedModel != out.DiscountedModel:\n", in.DiscountedModel(), out.DiscountedModel()) + return + } + if in.Engine().String() != out.Engine().String() { + fmt.Println("in.Engine != out.Engine:\n", in.Engine().String(), "\n", out.Engine().String()) + return + } + // Output: +} + +func ExampleCarToExtension() { + var data [256]byte + var hdr baseline.MessageHeader + var in baseline.Car + + makeCar(&hdr, &in, data[:], 0) + + in.WrapForDecode( + data[:], + hdr.EncodedLength(), + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data))-hdr.EncodedLength(), + ) + + var out extension.Car + out.WrapForDecode( + data[:], + hdr.EncodedLength(), + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data))-hdr.EncodedLength(), + ) + + if in.SerialNumber() != out.SerialNumber() { + fmt.Println("in.SerialNumber() != out.SerialNumber():\n", in.SerialNumber(), out.SerialNumber()) + return + } + if in.ModelYear() != out.ModelYear() { + fmt.Println("in.ModelYear() != out.ModelYear():\n", in.ModelYear(), out.ModelYear()) + return + } + + // Note casts so we can compare + if in.Available() != baseline.BooleanType(out.Available()) { + fmt.Println("in.Available != out.Available:\n", in.Available(), out.Available()) + return + } + if in.Code() != baseline.Model(out.Code()) { + fmt.Println("in.Code != out.Code:\n", in.Code(), out.Code()) + return + } + + if in.SomeNumbersLength() != out.SomeNumbersLength() { + fmt.Println("in.SomeNumbers != out.SomeNumbers:\n", in.SomeNumbersLength(), out.SomeNumbersLength()) + return + } + for i := uint64(0); i < uint64(in.SomeNumbersLength()); i++ { + if in.SomeNumbersIndex(i) != out.SomeNumbersIndex(i) { + fmt.Println("in.SomeNumbers != out.SomeNumbers:\n", in.SomeNumbersIndex(i), out.SomeNumbersIndex(i)) + return + } + } + + if in.GetVehicleCodeAsString() != out.GetVehicleCodeAsString() { + fmt.Println("in.VehicleCode != out.VehicleCode:\n", in.GetVehicleCodeAsString(), out.GetVehicleCodeAsString()) + return + } + if in.Extras().String() != out.Extras().String() { + fmt.Println("in.Extras != out.Extras:\n", in.Extras().String(), out.Extras().String()) + return + } + if extension.Model_C != out.DiscountedModel() { + fmt.Println("in.DiscountedModel != out.DiscountedModel:\n", in.DiscountedModel(), out.DiscountedModel()) + return + } + if in.Engine().String() != out.Engine().String() { + fmt.Println("in.Engine != out.Engine:\n", in.Engine().String(), "\n", out.Engine().String()) + return + } + + // Engine has two constant values which should come back filled in + if in.Engine().MaxRpm() != out.Engine().MaxRpm() { + fmt.Println("in.Engine.MaxRpm != out.Engine/MaxRpm:\n", + in.Engine().MaxRpm(), out.Engine().MaxRpm()) + return + } + + inFuelFigures, outFuelFigures := in.FuelFigures(), out.FuelFigures() + if inFuelFigures.Count() != outFuelFigures.Count() { + fmt.Println("in.FuelFiguresCount() != out.FuelFiguresCount():\n", + inFuelFigures.Count(), outFuelFigures.Count()) + return + } + for i := uint64(0); i < inFuelFigures.Count(); i++ { + inFuelFigures, outFuelFigures = inFuelFigures.Next(), outFuelFigures.Next() + if e, a := inFuelFigures.String(), outFuelFigures.String(); e != a { + fmt.Println("in.FuelFigures != out.FuelFigures:\n", e, a) + return + } + } + + inPerformanceFigures, outPerformanceFigures := in.PerformanceFigures(), out.PerformanceFigures() + if inPerformanceFigures.Count() != outPerformanceFigures.Count() { + fmt.Println("in.PerformanceFiguresCount() != out.PerformanceFiguresCount():\n", + inPerformanceFigures.Count(), outPerformanceFigures.Count()) + return + } + for i := uint64(0); i < inPerformanceFigures.Count(); i++ { + inPerformanceFigures, outPerformanceFigures = inPerformanceFigures.Next(), outPerformanceFigures.Next() + if e, a := inPerformanceFigures.String(), outPerformanceFigures.String(); e != a { + fmt.Println("in.PerformanceFigures != out.PerformanceFigures:\n", e, a) + return + } + } + + if e, a := in.Manufacturer(), out.Manufacturer(); e != a { + fmt.Println("in.Manufacturer != out.Manufacturer:\n", e, a) + return + } + + if e, a := in.Model(), out.Model(); e != a { + fmt.Println("in.Model != out.Model:\n", e, a) + return + } + + if e, a := in.ActivationCode(), out.ActivationCode(); e != a { + fmt.Println("in.ActivationCode != out.ActivationCode:\n", e, a) + return + } + + // Cupholder is not in example-schema and was introduced in + // extension-schema, so it should be NullValue + if out.CupHolderCount() != out.CupHolderCountNullValue() { + fmt.Println("out.cupholderCount not successfully nulled:\n", out.CupHolderCount()) + return + } + // Output: +} + +func ExampleExtensionToCar() { + var data [256]byte + var hdr extension.MessageHeader + var in extension.Car + + makeExtension(&hdr, &in, data[:], 0) + + in.WrapForDecode( + data[:], + hdr.EncodedLength(), + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data))-hdr.EncodedLength(), + ) + + var out baseline.Car + out.WrapForDecode( + data[:], + hdr.EncodedLength(), + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data))-hdr.EncodedLength(), + ) + + if in.SerialNumber() != out.SerialNumber() { + fmt.Println("in.SerialNumber() != out.SerialNumber():\n", in.SerialNumber(), out.SerialNumber()) + return + } + if in.ModelYear() != out.ModelYear() { + fmt.Println("in.ModelYear() != out.ModelYear():\n", in.ModelYear(), out.ModelYear()) + return + } + + // Note casts so we can compare + if in.Available() != extension.BooleanType(out.Available()) { + fmt.Println("in.Available != out.Available:\n", in.Available(), out.Available()) + return + } + if in.Code() != extension.Model(out.Code()) { + fmt.Println("in.Code != out.Code:\n", in.Code(), out.Code()) + return + } + + if in.SomeNumbersLength() != out.SomeNumbersLength() { + fmt.Println("in.SomeNumbers != out.SomeNumbers:\n", in.SomeNumbersLength(), out.SomeNumbersLength()) + return + } + for i := uint64(0); i < uint64(in.SomeNumbersLength()); i++ { + if in.SomeNumbersIndex(i) != out.SomeNumbersIndex(i) { + fmt.Println("in.SomeNumbers != out.SomeNumbers:\n", in.SomeNumbersIndex(i), out.SomeNumbersIndex(i)) + return + } + } + + if in.GetVehicleCodeAsString() != out.GetVehicleCodeAsString() { + fmt.Println("in.VehicleCode != out.VehicleCode:\n", in.GetVehicleCodeAsString(), out.GetVehicleCodeAsString()) + return + } + if in.Extras().String() != out.Extras().String() { + fmt.Println("in.Extras != out.Extras:\n", in.Extras().String(), out.Extras().String()) + return + } + if baseline.Model_C != out.DiscountedModel() { + fmt.Println("in.DiscountedModel != out.DiscountedModel:\n", in.DiscountedModel(), out.DiscountedModel()) + return + } + if in.Engine().String() != out.Engine().String() { + fmt.Println("in.Engine != out.Engine:\n", in.Engine().String(), "\n", out.Engine().String()) + return + } + + inFuelFigures, outFuelFigures := in.FuelFigures(), out.FuelFigures() + if inFuelFigures.Count() != outFuelFigures.Count() { + fmt.Println("in.FuelFiguresCount() != out.FuelFiguresCount():\n", + inFuelFigures.Count(), outFuelFigures.Count()) + return + } + for i := uint64(0); i < inFuelFigures.Count(); i++ { + inFuelFigures, outFuelFigures = inFuelFigures.Next(), outFuelFigures.Next() + if e, a := inFuelFigures.String(), outFuelFigures.String(); e != a { + fmt.Println("in.FuelFigures != out.FuelFigures:\n", e, a) + return + } + } + + inPerformanceFigures, outPerformanceFigures := in.PerformanceFigures(), out.PerformanceFigures() + if inPerformanceFigures.Count() != outPerformanceFigures.Count() { + fmt.Println("in.PerformanceFiguresCount() != out.PerformanceFiguresCount():\n", + inPerformanceFigures.Count(), outPerformanceFigures.Count()) + return + } + for i := uint64(0); i < inPerformanceFigures.Count(); i++ { + inPerformanceFigures, outPerformanceFigures = inPerformanceFigures.Next(), outPerformanceFigures.Next() + if e, a := inPerformanceFigures.String(), outPerformanceFigures.String(); e != a { + fmt.Println("in.PerformanceFigures != out.PerformanceFigures:\n", e, a) + return + } + } + + if e, a := in.ActivationCode(), out.ActivationCode(); e != a { + fmt.Println("in.ActivationCode != out.ActivationCode:\n", e, a) + return + } + // Output: +} + +// Helper to make a Car object as per the Java example +func makeCar(messageHeader *baseline.MessageHeader, car *baseline.Car, buffer []byte, bufferIndex uint64) { + messageHeader.Wrap(buffer, bufferIndex, uint64(messageHeader.SbeSchemaVersion()), uint64(len(buffer))) + + car.WrapAndApplyHeader(buffer, bufferIndex, uint64(len(buffer))-uint64(messageHeader.BlockLength())). + SetSerialNumber(1234). + SetModelYear(2013). + SetAvailable(baseline.BooleanType_T). + SetCode(baseline.Model_A). + PutVehicleCode(vehicleCode[:]) + + size := car.SomeNumbersLength() + for i := 0; i < size; i++ { + car.SetSomeNumbersIndex(uint64(i), uint32(i)) + } + + car.Extras().Clear(). + SetSportsPack(true). + SetSunRoof(true) + + car.Engine(). + SetCapacity(2000). + SetNumCylinders(4). + SetManufacturerCode(string(manufacturerCode[:])). + SetEfficiency(35). + SetBoosterEnabled(baseline.BooleanType_T). + Booster(). + SetBoostType(baseline.BoostType_NITROUS). + SetHorsePower(200) + + car.FuelFiguresCount(3).Next(). + SetSpeed(30).SetMpg(35.9).PutUsageDescription(string(urban[:])).Next(). + SetSpeed(55).SetMpg(49.0).PutUsageDescription(string(combined[:])).Next(). + SetSpeed(75).SetMpg(40.0).PutUsageDescription(string(highway[:])) + + perfFigures := car.PerformanceFiguresCount(2) + perfFigures.Next(). + SetOctaneRating(95). + AccelerationCount(3).Next(). + SetMph(30).SetSeconds(4.0).Next(). + SetMph(60).SetSeconds(7.5).Next(). + SetMph(100).SetSeconds(12.2) + perfFigures.Next(). + SetOctaneRating(99). + AccelerationCount(3).Next(). + SetMph(30).SetSeconds(3.8).Next(). + SetMph(60).SetSeconds(7.1).Next(). + SetMph(100).SetSeconds(11.8) + + car.PutManufacturer(string(manufacturer)) + car.PutModel(string(model)) + car.PutActivationCode(string(activationCode)) +} + +func makeExtension(messageHeader *extension.MessageHeader, car *extension.Car, buffer []byte, bufferIndex uint64) { + // Helper to make an Extension (car with cupholder) object + messageHeader.Wrap(buffer, bufferIndex, uint64(messageHeader.SbeSchemaVersion()), uint64(len(buffer))) + + car.WrapAndApplyHeader(buffer, bufferIndex, uint64(len(buffer))-uint64(messageHeader.BlockLength())). + SetSerialNumber(1234). + SetModelYear(2013). + SetAvailable(extension.BooleanType_T). + SetCode(extension.Model_A). + PutVehicleCode(vehicleCode[:]). + SetUuidIndex(0, 119). + SetUuidIndex(1, 120). + SetCupHolderCount(121) + + size := car.SomeNumbersLength() + for i := 0; i < size; i++ { + car.SetSomeNumbersIndex(uint64(i), uint32(i)) + } + + car.Extras().Clear(). + SetSportsPack(true). + SetSunRoof(true) + + car.Engine(). + SetCapacity(2000). + SetNumCylinders(4). + SetManufacturerCode(string(manufacturerCode[:])). + SetEfficiency(35). + SetBoosterEnabled(extension.BooleanType_T). + Booster(). + SetBoostType(extension.BoostType_NITROUS). + SetHorsePower(200) + + car.FuelFiguresCount(3).Next(). + SetSpeed(30).SetMpg(35.9).PutUsageDescription(string(urban[:])).Next(). + SetSpeed(55).SetMpg(49.0).PutUsageDescription(string(combined[:])).Next(). + SetSpeed(75).SetMpg(40.0).PutUsageDescription(string(highway[:])) + + perfFigures := car.PerformanceFiguresCount(2) + perfFigures.Next(). + SetOctaneRating(95). + AccelerationCount(3).Next(). + SetMph(30).SetSeconds(4.0).Next(). + SetMph(60).SetSeconds(7.5).Next(). + SetMph(100).SetSeconds(12.2) + perfFigures.Next(). + SetOctaneRating(99). + AccelerationCount(3).Next(). + SetMph(30).SetSeconds(3.8).Next(). + SetMph(60).SetSeconds(7.1).Next(). + SetMph(100).SetSeconds(11.8) + + car.PutManufacturer(string(manufacturer)) + car.PutModel(string(model)) + car.PutActivationCode(string(activationCode)) +} + +// The byte array can be made at ~rust/car_example/car_example_data.sbe by running gradlew generateCarExampleDataFile +// This can then be decoded using od -tu1 +var data = []byte{45, 0, 1, 0, 1, 0, 0, 0, 210, 4, 0, 0, 0, 0, 0, 0, 221, 7, 1, 65, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 97, 98, 99, 100, 101, 102, 6, 208, 7, 4, 49, 50, 51, 35, 1, 78, 200, 6, 0, 3, 0, 30, 0, 154, 153, 15, 66, 11, 0, 0, 0, 85, 114, 98, 97, 110, 32, 67, 121, 99, 108, 101, 55, 0, 0, 0, 68, 66, 14, 0, 0, 0, 67, 111, 109, 98, 105, 110, 101, 100, 32, 67, 121, 99, 108, 101, 75, 0, 0, 0, 32, 66, 13, 0, 0, 0, 72, 105, 103, 104, 119, 97, 121, 32, 67, 121, 99, 108, 101, 1, 0, 2, 0, 95, 6, 0, 3, 0, 30, 0, 0, 0, 128, 64, 60, 0, 0, 0, 240, 64, 100, 0, 51, 51, 67, 65, 99, 6, 0, 3, 0, 30, 0, 51, 51, 115, 64, 60, 0, 51, 51, 227, 64, 100, 0, 205, 204, 60, 65, 5, 0, 0, 0, 72, 111, 110, 100, 97, 9, 0, 0, 0, 67, 105, 118, 105, 99, 32, 86, 84, 105, 6, 0, 0, 0, 97, 98, 99, 100, 101, 102} diff --git a/gocode/src/flyweight/group/SBE tests/TestMessage1_test.go b/gocode/src/flyweight/group/SBE tests/TestMessage1_test.go new file mode 100644 index 0000000000..1525462f9f --- /dev/null +++ b/gocode/src/flyweight/group/SBE tests/TestMessage1_test.go @@ -0,0 +1,55 @@ +package sbe_tests + +import ( + "testing" +) + +func TestEncodeDecodeTestMessage1(t *testing.T) { + + var data [256]byte + var in TestMessage1 + in.WrapForEncode(data[:], 0, uint64(len(data))) + + in.SetTag1(44) + in.EntriesCount(1).Next(). + SetTagGroup1("abcdefghijklmnopqrst"). + SetTagGroup2(7) + + in.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + var out TestMessage1 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + if in.Tag1() != out.Tag1() { + t.Log("in != out:\n", in.Tag1(), out.Tag1()) + t.Fail() + } + inEntries, outEntries := in.Entries(), out.Entries() + if inEntries.Count() != outEntries.Count() { + t.Logf("len(in.Entries)(%d) != len(out.Entries)(%d):\n", inEntries.Count(), outEntries.Count()) + t.Fail() + } + for i := uint64(0); i < inEntries.Count(); i++ { + outEntries = outEntries.Next() + inEntries = inEntries.Next() + if inEntries.String() != outEntries.String() { + t.Logf("in.Entries[%d] != out.Entries[%d]: %s %s\n", i, i, inEntries.String(), outEntries.String()) + t.Fail() + } + } + + return + +} diff --git a/gocode/src/flyweight/group_with_data/TestMessages_test.go b/gocode/src/flyweight/group_with_data/TestMessages_test.go new file mode 100644 index 0000000000..73d8066e3f --- /dev/null +++ b/gocode/src/flyweight/group_with_data/TestMessages_test.go @@ -0,0 +1,200 @@ +package group_with_data + +import ( + _ "fmt" + "testing" +) + +func TestEncodeDecodeTestMessage1(t *testing.T) { + var data [256]byte + var in TestMessage1 + in.WrapForEncode(data[:], 0, uint64(len(data))) + + in.SetTag1(1234) + in.EntriesCount(1).Next(). + SetTagGroup1("123456789"). + SetTagGroup2(123456789). + PutVarDataField("abcdef") + + var out TestMessage1 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + expected := `{"Name": "TestMessage1", "sbeTemplateId": 1, "Tag1": "1234", "Entries": [{"TagGroup1": "123456789", "TagGroup2": "123456789", "varDataField": "abcdef"}]}` + if actual := out.String(); actual != expected { + t.Logf("Failed to decode, expected %s, got %s", expected, actual) + t.Fail() + } +} + +func TestEncodeDecodeTestMessage2(t *testing.T) { + var data [256]byte + var in TestMessage2 + in.WrapForEncode(data[:], 0, uint64(len(data))) + + tag1 := uint32(1234) + tagGroup1 := "123456789" + tagGroup2 := int64(123456789) + varDataField1 := "abcdef" + varDataField2 := "ghij" + + in.SetTag1(tag1) + entries := in.EntriesCount(2) + for i := 0; i < 2; i++ { + entry := entries.Next() + entry.SetTagGroup1(tagGroup1). + SetTagGroup2(tagGroup2). + PutVarDataField1(varDataField1). + PutVarDataField2(varDataField2) + } + + var out TestMessage2 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + if out.Tag1() != tag1 { + t.Logf("Tag1 failed, expected %d, got %d", tag1, out.Tag1()) + t.Fail() + } + outEntries := out.Entries() + for i := 0; i < 2; i++ { + entry := outEntries.Next() + if entry.GetTagGroup1AsString() != tagGroup1 { + t.Logf("TagGroup1 failed at index %d, expected %s, got %s", i, tagGroup1, entry.GetTagGroup1AsString()) + t.Fail() + } + if entry.TagGroup2() != tagGroup2 { + t.Logf("TagGroup2 failed at index %d, expected %d, got %d", i, tagGroup2, entry.TagGroup2()) + t.Fail() + } + if entry.VarDataField1() != varDataField1 { + t.Logf("VarDataField1 failed at index %d, expected %s, got %s", i, varDataField1, entry.VarDataField1()) + t.Fail() + } + if entry.VarDataField2() != varDataField2 { + t.Logf("VarDataField2 failed at index %d, expected %s, got %s", i, varDataField2, entry.VarDataField2()) + t.Fail() + } + } +} + +func TestEncodeDecodeTestMessage3(t *testing.T) { + var data [256]byte + var in TestMessage3 + in.WrapForEncode(data[:], 0, uint64(len(data))) + + tag1 := uint32(1234) + tagGroup1 := "123456789" + varDataField := "middle" + tagGroup2Nested := int64(99887766) + varDataFieldNested := "nested" + + in.SetTag1(tag1) + entries := in.EntriesCount(2) + for i := 0; i < 2; i++ { + entry := entries.Next() + entry.SetTagGroup1(tagGroup1). + PutVarDataField(varDataField). + NestedEntriesCount(1).Next(). + SetTagGroup2(tagGroup2Nested). + PutVarDataFieldNested(varDataFieldNested) + } + + in.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + var out TestMessage3 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + if out.Tag1() != tag1 { + t.Logf("Tag1 failed, expected %d, got %d", tag1, out.Tag1()) + t.Fail() + } + outEntries := out.Entries() + for i := 0; i < 2; i++ { + entry := outEntries.Next() + if entry.GetTagGroup1AsString() != tagGroup1 { + t.Logf("TagGroup1 failed at index %d, expected %s, got %s", i, tagGroup1, entry.GetTagGroup1AsString()) + t.Fail() + } + if entry.VarDataField() != varDataField { + t.Logf("VarDataField failed at index %d, expected %s, got %s", i, varDataField, entry.VarDataField()) + t.Fail() + } + nestedEntry := entry.NestedEntries().Next() + if nestedEntry.TagGroup2() != tagGroup2Nested { + t.Logf("TagGroup2Nested failed at index %d, expected %d, got %d", i, tagGroup2Nested, nestedEntry.TagGroup2()) + t.Fail() + } + if nestedEntry.VarDataFieldNested() != varDataFieldNested { + t.Logf("VarDataFieldNested failed at index %d, expected %s, got %s", i, varDataFieldNested, nestedEntry.VarDataFieldNested()) + t.Fail() + } + } +} + +func TestEncodeDecodeTestMessage4(t *testing.T) { + var data [256]byte + var in TestMessage4 + in.WrapForEncode(data[:], 0, uint64(len(data))) + + tag1 := uint32(9876) + varDataField1 := "abcdef" + varDataField2 := "ghij" + + in.SetTag1(tag1) + entries := in.EntriesCount(3) + for i := 0; i < 3; i++ { + entry := entries.Next() + entry.PutVarDataField1(varDataField1). + PutVarDataField2(varDataField2) + } + + var out TestMessage4 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + if out.Tag1() != tag1 { + t.Logf("Tag1 failed, expected %d, got %d", tag1, out.Tag1()) + t.Fail() + } + + outEntries := out.Entries() + for i := 0; i < 3; i++ { + entry := outEntries.Next() + if entry.VarDataField1() != varDataField1 { + t.Logf("VarDataField1 failed at index %d, expected %s, got %s", i, varDataField1, entry.VarDataField1()) + t.Fail() + } + if entry.VarDataField2() != varDataField2 { + t.Logf("VarDataField2 failed at index %d, expected %s, got %s", i, varDataField2, entry.VarDataField2()) + t.Fail() + } + } +} diff --git a/gocode/src/flyweight/group_with_data_extension/Extension_test.go b/gocode/src/flyweight/group_with_data_extension/Extension_test.go new file mode 100644 index 0000000000..afce620e0c --- /dev/null +++ b/gocode/src/flyweight/group_with_data_extension/Extension_test.go @@ -0,0 +1,210 @@ +package group_with_data_extension + +import ( + "flyweight/group_with_data" + "fmt" + "math" + "testing" +) + +// Note, this is a copy of group-with-data that we extended to test +// message extensions within a nested group and vardata +func makeTestMessage3Extension() (data []byte, msg *TestMessage3) { + data = make([]byte, 256) + var t TestMessage3 + t.WrapForEncode(data[:], 0, uint64(len(data))) + + t.SetTag1(1234) + + entries := t.EntriesCount(2) // Assuming 2 entries + for i := 0; i < 2; i++ { + entry := entries.Next() + entry.SetTagGroup1("123456789") + + nestedEntries := entry.NestedEntriesCount(1) // Assuming 1 nested entry + nestedEntry := nestedEntries.Next() + nestedEntry.SetTagGroup2(99887766) + nestedEntry.SetInnerExtension(11112222) + nestedEntry.PutVarDataFieldNested("nested") + + entry.PutVarDataField("middle") + } + + return data, &t +} + +func makeTestMessage3Original() (data []byte, msg *group_with_data.TestMessage3) { + data = make([]byte, 256) + var t group_with_data.TestMessage3 + t.WrapForEncode(data[:], 0, uint64(len(data))) + + t.SetTag1(1234) + + entries := t.EntriesCount(2) // Assuming 2 entries + for i := 0; i < 2; i++ { + entry := entries.Next() + entry.SetTagGroup1("123456789") + + nestedEntries := entry.NestedEntriesCount(1) // Assuming 1 nested entry + nestedEntry := nestedEntries.Next() + nestedEntry.SetTagGroup2(99887766) + nestedEntry.PutVarDataFieldNested("") + + entry.PutVarDataField("middle") + } + + return data, &t +} + +// Basic test of new message +func TestEncodeDecodeNewtoNew(t *testing.T) { + data, in := makeTestMessage3Extension() + + var out TestMessage3 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + if in.Tag1() != out.Tag1() { + t.Logf("in.Tag1 != out.Tag1") + t.Fail() + } + + entries := out.Entries() + for i := 0; i < int(entries.Count()); i++ { + entry := entries.Next() + if entry.GetTagGroup1AsString() != "123456789" { + t.Logf("entry.TagGroup1 != out.Entries[0].TagGroup1") + fmt.Printf("%+v\n%+v\n", in, out) + t.Fail() + } + + nestedEntry := entry.NestedEntries().Next() + if nestedEntry.TagGroup2() != 99887766 { + t.Logf("nestedEntry.TagGroup2 != out.Entries[0].NestedEntries[0].TagGroup2") + fmt.Printf("%+v\n%+v\n", in, out) + t.Fail() + } + + if nestedEntry.InnerExtension() != 11112222 { + t.Logf("n.Entries[0].NestedEntries[0].InnerExtension != out.Entries[0].NestedEntries[0].InnerExtension") + fmt.Printf("%+v\n%+v\n", in, out) + t.Fail() + } + + if nestedEntry.VarDataFieldNested() != "nested" { + t.Logf("in.Entries[0].NestedEntries[0].VarDataFieldNested != out.Entries[0].NestedEntries[0].VarDataFieldNested") + fmt.Printf("%+v\n%+v\n", in, out) + t.Fail() + } + + if value := entry.VarDataField(); value != "middle" { + t.Logf("in.Entries[%d].VarDataField (%v) != entry.VarDataField (%v)", i, value, "middle") + t.Fail() + } + } +} + +// Test of New to Old +func TestEncodeDecodeNewToOld(t *testing.T) { + data, in := makeTestMessage3Extension() + + var out group_with_data.TestMessage3 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + if in.Tag1() != out.Tag1() { + t.Logf("in.Tag1 != out.Tag1") + t.Fail() + } + + entries := out.Entries() + for i := 0; i < int(entries.Count()); i++ { + entry := entries.Next() + if value := entry.GetTagGroup1AsString(); value != "123456789" { + t.Logf("entry.TagGroup1 (%s) != out.Entries[0].TagGroup1 (%s)", + value, "123456789") + t.Fail() + } + + nestedEntry := entry.NestedEntries().Next() + if nestedEntry.TagGroup2() != 99887766 { + t.Logf("nestedEntry.TagGroup2 (%d) != out.Entries[0].NestedEntries[0].TagGroup2 (%d)", + nestedEntry.TagGroup2(), 99887766) + t.Fail() + } + + if nestedEntry.VarDataFieldNested() != "nested" { + t.Logf("in.Entries[0].NestedEntries[0].VarDataFieldNested (%s) != out.Entries[0].NestedEntries[0].VarDataFieldNested (%s)", + nestedEntry.VarDataFieldNested(), "nested") + t.Fail() + } + + if value := entry.VarDataField(); value != "middle" { + t.Logf("in.Entries[%d].VarDataField (%v) != entry.VarDataField (%v)", i, value, "middle") + t.Fail() + } + } +} + +// Test of Old to New +func TestEncodeDecodeOldToNew(t *testing.T) { + data, in := makeTestMessage3Original() + + var out TestMessage3 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + if in.Tag1() != out.Tag1() { + t.Logf("in.Tag1 != out.Tag1") + t.Fail() + } + + entries := out.Entries() + for i := 0; i < int(entries.Count()); i++ { + entry := entries.Next() + if entry.GetTagGroup1AsString() != "123456789" { + t.Logf("entry.TagGroup1 != out.Entries[0].TagGroup1") + fmt.Printf("%+v\n%+v\n", in, out) + t.Fail() + } + + nestedEntry := entry.NestedEntries().Next() + if nestedEntry.TagGroup2() != 99887766 { + t.Logf("nestedEntry.TagGroup2 (%d) != out.Entries[0].NestedEntries[0].TagGroup2 (%d)", + nestedEntry.TagGroup2(), 99887766) + t.Fail() + } + + if nestedEntry.InnerExtension() != math.MinInt64 { + t.Logf("n.Entries[0].NestedEntries[0].InnerExtension (%d) != out.Entries[0].NestedEntries[0].InnerExtension (%d)", + nestedEntry.InnerExtension(), math.MinInt64) + t.Fail() + } + + if value := nestedEntry.VarDataFieldNested(); value != "" { + t.Logf("in.Entries[0].NestedEntries[0].VarDataFieldNested (%s) != out.Entries[0].NestedEntries[0].VarDataFieldNested (%s)", + value, "") + t.Fail() + } + + if value := entry.VarDataField(); value != "middle" { + t.Logf("in.Entries[%d].VarDataField (%v) != entry.VarDataField (%v)", i, value, "middle") + t.Fail() + } + } +} diff --git a/gocode/src/flyweight/issue435/Issue435_test.go b/gocode/src/flyweight/issue435/Issue435_test.go new file mode 100644 index 0000000000..170967fc28 --- /dev/null +++ b/gocode/src/flyweight/issue435/Issue435_test.go @@ -0,0 +1,48 @@ +package issue435 + +import ( + "testing" +) + +func TestEncodeDecode(t *testing.T) { + + var data [256]byte + + var in Issue435 + + // Non-standard header so we use the generated one + var hdr MessageHeader + hdr.Wrap(data[:], 0, 0, uint64(len(data))) + hdr.S().SetTwo(true) + + in.WrapForEncode(data[:], hdr.EncodedLength(), uint64(len(data))-hdr.EncodedLength()) + in.Example().SetE(EnumRef_Two) + + var outHdr MessageHeader + outHdr.Wrap(data[:], 0, 0, uint64(len(data))) + + if !outHdr.S().Two() { + t.Logf("Failed to decode, expected Two to be true") + t.Fail() + } + + var out Issue435 + + out.WrapForDecode( + data[:], + hdr.EncodedLength(), + uint64(outHdr.BlockLength()), + outHdr.ActingVersion(), + uint64(len(data))) + + if out.Example().E() != EnumRef_Two { + t.Logf("Failed to decode, expected EnumRef_Two, got %d", out.Example().E()) + t.Fail() + } + + expected := `{"Name": "Issue435", "sbeTemplateId": 1, "example": {"e": "Two"}}` + if actual := out.String(); actual != expected { + t.Logf("Failed to decode, expected %s, got %s", expected, actual) + t.Fail() + } +} diff --git a/gocode/src/flyweight/issue472/Issue472_test.go b/gocode/src/flyweight/issue472/Issue472_test.go new file mode 100644 index 0000000000..151dd62ccc --- /dev/null +++ b/gocode/src/flyweight/issue472/Issue472_test.go @@ -0,0 +1,27 @@ +package issue472 + +import ( + "testing" +) + +func TestEncodeDecode(t *testing.T) { + var data [256]byte + // in contains a single optional field which is not initialized + var in Issue472 + in.WrapForEncode(data[:], 0, uint64(len(data))) + + var out Issue472 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + expected := `{"Name": "Issue472", "sbeTemplateId": 1, "optional": "0"}` + if actual := out.String(); actual != expected { + t.Logf("Failed to decode, expected %s, got %s", expected, actual) + t.Fail() + } +} diff --git a/gocode/src/flyweight/issue483/Issue483_test.go b/gocode/src/flyweight/issue483/Issue483_test.go new file mode 100644 index 0000000000..7af235b396 --- /dev/null +++ b/gocode/src/flyweight/issue483/Issue483_test.go @@ -0,0 +1,35 @@ +package issue483 + +import ( + "testing" +) + +func TestPresence(t *testing.T) { + var issue483 Issue483 + + if issue483.UnsetMetaAttribute("PRESENCE") != "required" { + t.Log("Unset attribute's presence should be 'required'") + t.Fail() + } + + if issue483.RequiredMetaAttribute("PRESENCE") != "required" { + t.Log("Required attribute's presence should be 'required'") + t.Fail() + } + + if issue483.ConstantMetaAttribute("PRESENCE") != "constant" { + t.Log("Constant attribute's presence should be 'constant'") + t.Fail() + } + + // Check contant value is set by init func + if issue483.Constant() != 1 { + t.Log("Constant's value should be 1") + t.Fail() + } + + if issue483.OptionalMetaAttribute("PRESENCE") != "optional" { + t.Log("Optional attribute's presence should be 'optional'") + t.Fail() + } +} diff --git a/gocode/src/flyweight/issue488/Issue488_test.go b/gocode/src/flyweight/issue488/Issue488_test.go new file mode 100644 index 0000000000..5a01a98a15 --- /dev/null +++ b/gocode/src/flyweight/issue488/Issue488_test.go @@ -0,0 +1,9 @@ +package issue488 + +import ( + "testing" +) + +func TestPresence(t *testing.T) { + // placeholder, really just looking for clean builds +} diff --git a/gocode/src/flyweight/issue560/Issue560_test.go b/gocode/src/flyweight/issue560/Issue560_test.go new file mode 100644 index 0000000000..48756aff24 --- /dev/null +++ b/gocode/src/flyweight/issue560/Issue560_test.go @@ -0,0 +1,28 @@ +package issue560 + +import ( + "testing" +) + +func TestEncodeDecode(t *testing.T) { + var data [256]byte + + // in contains a single optional field which is not initialized + var in Issue560 + in.WrapForEncode(data[:], 0, uint64(len(data))) + + var out Issue560 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + expected := `{"Name": "Issue560", "sbeTemplateId": 1, "discountedModel": "C"}` + if actual := out.String(); actual != expected { + t.Logf("Failed to decode, expected %s, got %s", expected, actual) + t.Fail() + } +} diff --git a/gocode/src/issue847/issue847_test.go b/gocode/src/flyweight/issue847/issue847_test.go similarity index 100% rename from gocode/src/issue847/issue847_test.go rename to gocode/src/flyweight/issue847/issue847_test.go diff --git a/gocode/src/issue848/issue848_test.go b/gocode/src/flyweight/issue848/issue848_test.go similarity index 100% rename from gocode/src/issue848/issue848_test.go rename to gocode/src/flyweight/issue848/issue848_test.go diff --git a/gocode/src/issue849/issue849_test.go b/gocode/src/flyweight/issue849/issue849_test.go similarity index 100% rename from gocode/src/issue849/issue849_test.go rename to gocode/src/flyweight/issue849/issue849_test.go diff --git a/gocode/src/mktdata/fixme_test.go b/gocode/src/flyweight/mktdata/fixme_test.go similarity index 100% rename from gocode/src/mktdata/fixme_test.go rename to gocode/src/flyweight/mktdata/fixme_test.go diff --git a/gocode/src/flyweight/simple/simple_test.go b/gocode/src/flyweight/simple/simple_test.go new file mode 100644 index 0000000000..b15a3c2bad --- /dev/null +++ b/gocode/src/flyweight/simple/simple_test.go @@ -0,0 +1,46 @@ +package simple + +import ( + "testing" +) + +func TestEncodeDecode(t *testing.T) { + var data [256]byte + var in Simple0 + in.WrapForEncode(data[:], 0, uint64(len(data))) + + in.SetU64nv(2863311530) + in.SetU64(2863311530) + in.SetU32(123456) + in.SetU16(7890) + in.SetU8(63) + in.SetS8(-8) + in.SetS16(-16) + in.SetS32(-32) + in.SetS64(-64) + in.SetF32(3.14) + in.SetD64(-3.14e7) + in.SetString6ASCII("abcdef") + in.SetString1ASCII('A') + in.PutInt2Values(254, 255) + + var out Simple0 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + expected := `{"Name": "Simple0", "sbeTemplateId": 11, "U64nv": "2863311530", "U64": "2863311530", "U32": "123456", "U16": "7890", "U8": "63", "S8": "-8", "S16": "-16", "S32": "-32", "S64": "-64", "F32": "3.14", "D64": "-3.14e+07", "String6ASCII": "abcdef", "String1ASCII": "A", "Int2": [254,255]}` + if actual := out.String(); actual != expected { + t.Logf("Failed to decode, expected %s, got %s", expected, actual) + t.Fail() + } + + if in.U64SinceVersion() != 1 { + t.Log("in.U64SinceVersion() should be 1 and is", in.U64SinceVersion()) + t.Fail() + } +} diff --git a/gocode/src/flyweight/since-deprecated/SinceDeprecated_test.go b/gocode/src/flyweight/since-deprecated/SinceDeprecated_test.go new file mode 100644 index 0000000000..1dcf5f416d --- /dev/null +++ b/gocode/src/flyweight/since-deprecated/SinceDeprecated_test.go @@ -0,0 +1,23 @@ +package since_deprecated + +import ( + "testing" +) + +func TestSinceDeprecated(t *testing.T) { + + sv := SinceDeprecated{} + + // V1 has always been there + if sv.V1SinceVersion() != 0 { + t.Fail() + } + // V2 was introduced in version 2 + if sv.V2SinceVersion() != 2 { + t.Fail() + } + // V3 was introduced in version 3 and deprecated in version 4 + if sv.V3SinceVersion() != 3 { + t.Fail() + } +} diff --git a/gocode/src/flyweight/vardata/SBE tests/TestMessage1_test.go b/gocode/src/flyweight/vardata/SBE tests/TestMessage1_test.go new file mode 100644 index 0000000000..237ddb7d5c --- /dev/null +++ b/gocode/src/flyweight/vardata/SBE tests/TestMessage1_test.go @@ -0,0 +1,27 @@ +package sbe_tests + +import ( + "testing" +) + +func TestEncodeDecodeTestMessage1(t *testing.T) { + var data [256]byte + var in TestMessage1 + in.WrapForEncode(data[:], 0, uint64(len(data))) + in.PutEncryptedNewPassword("abcdefghijklmnopqrst") + + var out TestMessage1 + out.WrapForDecode( + data[:], + 0, + uint64(in.SbeBlockLength()), + uint64(in.SbeSchemaVersion()), + uint64(len(data)), + ) + + expected := `{"Name": "TestMessage1", "sbeTemplateId": 1, "encryptedNewPassword": "abcdefghijklmnopqrst"}` + if actual := out.String(); actual != expected { + t.Logf("Failed to decode, expected %s, got %s", expected, actual) + t.Fail() + } +} diff --git a/gocode/src/baseline-bigendian/Car_test.go b/gocode/src/struct/baseline-bigendian/Car_test.go similarity index 100% rename from gocode/src/baseline-bigendian/Car_test.go rename to gocode/src/struct/baseline-bigendian/Car_test.go diff --git a/gocode/src/baseline/Car_test.go b/gocode/src/struct/baseline/Car_test.go similarity index 100% rename from gocode/src/baseline/Car_test.go rename to gocode/src/struct/baseline/Car_test.go diff --git a/gocode/src/basic/SBE tests/ENUM_test.go b/gocode/src/struct/basic/SBE tests/ENUM_test.go similarity index 100% rename from gocode/src/basic/SBE tests/ENUM_test.go rename to gocode/src/struct/basic/SBE tests/ENUM_test.go diff --git a/gocode/src/basic/SBE tests/Message1_test.go b/gocode/src/struct/basic/SBE tests/Message1_test.go similarity index 100% rename from gocode/src/basic/SBE tests/Message1_test.go rename to gocode/src/struct/basic/SBE tests/Message1_test.go diff --git a/gocode/src/basic/SBE tests/SET_test.go b/gocode/src/struct/basic/SBE tests/SET_test.go similarity index 100% rename from gocode/src/basic/SBE tests/SET_test.go rename to gocode/src/struct/basic/SBE tests/SET_test.go diff --git a/gocode/src/composite/Composite_test.go b/gocode/src/struct/composite/Composite_test.go similarity index 100% rename from gocode/src/composite/Composite_test.go rename to gocode/src/struct/composite/Composite_test.go diff --git a/gocode/src/composite_elements/Message_test.go b/gocode/src/struct/composite_elements/Message_test.go similarity index 100% rename from gocode/src/composite_elements/Message_test.go rename to gocode/src/struct/composite_elements/Message_test.go diff --git a/gocode/src/example-schema/CarExample.go b/gocode/src/struct/example-schema/CarExample.go similarity index 100% rename from gocode/src/example-schema/CarExample.go rename to gocode/src/struct/example-schema/CarExample.go diff --git a/gocode/src/example-schema/CarExample_test.go b/gocode/src/struct/example-schema/CarExample_test.go similarity index 100% rename from gocode/src/example-schema/CarExample_test.go rename to gocode/src/struct/example-schema/CarExample_test.go diff --git a/gocode/src/example-socket-clientserver/clientserver.go b/gocode/src/struct/example-socket-clientserver/clientserver.go similarity index 100% rename from gocode/src/example-socket-clientserver/clientserver.go rename to gocode/src/struct/example-socket-clientserver/clientserver.go diff --git a/gocode/src/group/SBE tests/TestMessage1_test.go b/gocode/src/struct/group/SBE tests/TestMessage1_test.go similarity index 100% rename from gocode/src/group/SBE tests/TestMessage1_test.go rename to gocode/src/struct/group/SBE tests/TestMessage1_test.go diff --git a/gocode/src/group_with_data/TestMessages_test.go b/gocode/src/struct/group_with_data/TestMessages_test.go similarity index 100% rename from gocode/src/group_with_data/TestMessages_test.go rename to gocode/src/struct/group_with_data/TestMessages_test.go diff --git a/gocode/src/group_with_data_extension/Extension_test.go b/gocode/src/struct/group_with_data_extension/Extension_test.go similarity index 99% rename from gocode/src/group_with_data_extension/Extension_test.go rename to gocode/src/struct/group_with_data_extension/Extension_test.go index cb245d64d8..0c79daafb8 100644 --- a/gocode/src/group_with_data_extension/Extension_test.go +++ b/gocode/src/struct/group_with_data_extension/Extension_test.go @@ -3,8 +3,8 @@ package group_with_data_extension import ( "bytes" "fmt" - "group_with_data" "math" + "struct/group_with_data" "testing" ) diff --git a/gocode/src/issue435/Issue435_test.go b/gocode/src/struct/issue435/Issue435_test.go similarity index 100% rename from gocode/src/issue435/Issue435_test.go rename to gocode/src/struct/issue435/Issue435_test.go diff --git a/gocode/src/issue472/Issue472_test.go b/gocode/src/struct/issue472/Issue472_test.go similarity index 100% rename from gocode/src/issue472/Issue472_test.go rename to gocode/src/struct/issue472/Issue472_test.go diff --git a/gocode/src/issue483/Issue483_test.go b/gocode/src/struct/issue483/Issue483_test.go similarity index 100% rename from gocode/src/issue483/Issue483_test.go rename to gocode/src/struct/issue483/Issue483_test.go diff --git a/gocode/src/issue488/Issue488_test.go b/gocode/src/struct/issue488/Issue488_test.go similarity index 100% rename from gocode/src/issue488/Issue488_test.go rename to gocode/src/struct/issue488/Issue488_test.go diff --git a/gocode/src/issue560/Issue560_test.go b/gocode/src/struct/issue560/Issue560_test.go similarity index 100% rename from gocode/src/issue560/Issue560_test.go rename to gocode/src/struct/issue560/Issue560_test.go diff --git a/gocode/src/struct/issue847/issue847_test.go b/gocode/src/struct/issue847/issue847_test.go new file mode 100644 index 0000000000..1fb14887ad --- /dev/null +++ b/gocode/src/struct/issue847/issue847_test.go @@ -0,0 +1,9 @@ +package issue847 + +import ( + "testing" +) + +func TestNothing(t *testing.T) { + // placeholder, really just looking for clean builds +} diff --git a/gocode/src/struct/issue848/issue848_test.go b/gocode/src/struct/issue848/issue848_test.go new file mode 100644 index 0000000000..1106e74b01 --- /dev/null +++ b/gocode/src/struct/issue848/issue848_test.go @@ -0,0 +1,9 @@ +package issue848 + +import ( + "testing" +) + +func TestNothing(t *testing.T) { + // placeholder, really just looking for clean builds +} diff --git a/gocode/src/struct/issue849/issue849_test.go b/gocode/src/struct/issue849/issue849_test.go new file mode 100644 index 0000000000..0149615810 --- /dev/null +++ b/gocode/src/struct/issue849/issue849_test.go @@ -0,0 +1,9 @@ +package issue849 + +import ( + "testing" +) + +func TestNothing(t *testing.T) { + // placeholder, really just looking for clean builds +} diff --git a/gocode/src/struct/mktdata/fixme_test.go b/gocode/src/struct/mktdata/fixme_test.go new file mode 100644 index 0000000000..6792aa42e5 --- /dev/null +++ b/gocode/src/struct/mktdata/fixme_test.go @@ -0,0 +1,10 @@ +package mktdata + +import ( + "fmt" + "testing" +) + +func TestFixme(t *testing.T) { + fmt.Println("No tests implemented here") +} diff --git a/gocode/src/mktdata/preallocated_test.go b/gocode/src/struct/mktdata/preallocated_test.go similarity index 100% rename from gocode/src/mktdata/preallocated_test.go rename to gocode/src/struct/mktdata/preallocated_test.go diff --git a/gocode/src/simple/simple_test.go b/gocode/src/struct/simple/simple_test.go similarity index 100% rename from gocode/src/simple/simple_test.go rename to gocode/src/struct/simple/simple_test.go diff --git a/gocode/src/since-deprecated/SinceDeprecated_test.go b/gocode/src/struct/since-deprecated/SinceDeprecated_test.go similarity index 100% rename from gocode/src/since-deprecated/SinceDeprecated_test.go rename to gocode/src/struct/since-deprecated/SinceDeprecated_test.go diff --git a/gocode/src/vardata/SBE tests/TestMessage1_test.go b/gocode/src/struct/vardata/SBE tests/TestMessage1_test.go similarity index 100% rename from gocode/src/vardata/SBE tests/TestMessage1_test.go rename to gocode/src/struct/vardata/SBE tests/TestMessage1_test.go diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/SbeTool.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/SbeTool.java index 3d95be6172..9c0ab8df4b 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/SbeTool.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/SbeTool.java @@ -242,6 +242,12 @@ public class SbeTool */ public static final String JAVA_PRECEDENCE_CHECKS_PROPERTY_NAME = "sbe.java.precedence.checks.property.name"; + /** + * Boolean system property to generate flyweights instead of structs in generated Go code. + * Defaults to false + */ + public static final String GO_GENERATE_FLYWEIGHTS = "sbe.go.generate.generate.flyweights"; + /** * Main entry point for the SBE Tool. * diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/TargetCodeGeneratorLoader.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/TargetCodeGeneratorLoader.java index c031ad60a8..3ff3895cd7 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/TargetCodeGeneratorLoader.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/TargetCodeGeneratorLoader.java @@ -21,8 +21,10 @@ import uk.co.real_logic.sbe.generation.cpp.CppDtoGenerator; import uk.co.real_logic.sbe.generation.cpp.CppGenerator; import uk.co.real_logic.sbe.generation.cpp.NamespaceOutputManager; -import uk.co.real_logic.sbe.generation.golang.GolangGenerator; -import uk.co.real_logic.sbe.generation.golang.GolangOutputManager; +import uk.co.real_logic.sbe.generation.golang.struct.GolangGenerator; +import uk.co.real_logic.sbe.generation.golang.struct.GolangOutputManager; +import uk.co.real_logic.sbe.generation.golang.flyweight.GolangFlyweightGenerator; +import uk.co.real_logic.sbe.generation.golang.flyweight.GolangFlyweightOutputManager; import uk.co.real_logic.sbe.generation.java.JavaDtoGenerator; import uk.co.real_logic.sbe.generation.java.JavaGenerator; import uk.co.real_logic.sbe.generation.java.JavaOutputManager; @@ -126,7 +128,19 @@ public CodeGenerator newInstance(final Ir ir, final String outputDir) */ public CodeGenerator newInstance(final Ir ir, final String outputDir) { - return new GolangGenerator(ir, new GolangOutputManager(outputDir, ir.applicableNamespace())); + if ("true".equals(System.getProperty(GO_GENERATE_FLYWEIGHTS))) + { + return new GolangFlyweightGenerator( + ir, + "true".equals(System.getProperty(DECODE_UNKNOWN_ENUM_VALUES)), + new GolangFlyweightOutputManager(outputDir, ir.applicableNamespace())); + } + else + { + return new GolangGenerator( + ir, + new GolangOutputManager(outputDir, ir.applicableNamespace())); + } } }, diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java new file mode 100644 index 0000000000..55b0265c94 --- /dev/null +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java @@ -0,0 +1,3286 @@ +/* + * Copyright 2013-2023 Real Logic Limited. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package uk.co.real_logic.sbe.generation.golang.flyweight; + +import org.agrona.Strings; +import org.agrona.Verify; +import org.agrona.generation.OutputManager; +import uk.co.real_logic.sbe.PrimitiveType; +import uk.co.real_logic.sbe.generation.CodeGenerator; +import uk.co.real_logic.sbe.generation.Generators; +import uk.co.real_logic.sbe.ir.Encoding; +import uk.co.real_logic.sbe.ir.Ir; +import uk.co.real_logic.sbe.ir.Signal; +import uk.co.real_logic.sbe.ir.Token; + +import java.io.IOException; +import java.io.Writer; +import java.nio.ByteOrder; +import java.util.*; + +import static uk.co.real_logic.sbe.generation.Generators.toUpperFirstChar; +import static uk.co.real_logic.sbe.generation.golang.flyweight.GolangFlyweightUtil.*; +import static uk.co.real_logic.sbe.ir.GenerationUtil.*; + +/** + * Codec generator for the Go programming language using flyweights. + */ +@SuppressWarnings("MethodLength") +public class GolangFlyweightGenerator implements CodeGenerator +{ + private static final String BASE_INDENT = ""; + private static final String INDENT = " "; + + private final Ir ir; + private final OutputManager outputManager; + private final boolean shouldDecodeUnknownEnumValues; + + private final Set includes = new TreeSet<>(); + + /** + * Create a new Golang language {@link CodeGenerator}. + * + * @param ir for the messages and types. + * @param shouldDecodeUnknownEnumValues generate support for unknown enum values when decoding. + * @param outputManager for generating the codecs to. + */ + public GolangFlyweightGenerator(final Ir ir, final boolean shouldDecodeUnknownEnumValues, final OutputManager outputManager) + { + Verify.notNull(ir, "ir"); + Verify.notNull(outputManager, "outputManager"); + + this.ir = ir; + this.shouldDecodeUnknownEnumValues = shouldDecodeUnknownEnumValues; + this.outputManager = outputManager; + } + + private static void generateGroupClassHeader( + final StringBuilder sb, + final String groupClassName, + final List tokens, + final int index, + final String indent, + final List fields, + final List groups) + { + final String dimensionsClassName = formatClassName(tokens.get(index + 1).name()); + final int dimensionHeaderLength = tokens.get(index + 1).encodedLength(); + final int blockLength = tokens.get(index).encodedLength(); + final Token blockLengthToken = Generators.findFirst("blockLength", tokens, index); + final Token numInGroupToken = Generators.findFirst("numInGroup", tokens, index); + final String golangTypeBlockLength = goTypeName(blockLengthToken.encoding().primitiveType()); + final String golangTypeNumInGroup = goTypeName(numInGroupToken.encoding().primitiveType()); + + new Formatter(sb).format("\n" + + indent + "type %1$s struct {\n" + + indent + " buffer []byte\n" + + indent + " bufferLength uint64\n" + + indent + " initialPosition uint64\n" + + indent + " positionPtr *uint64\n" + + indent + " blockLength uint64\n" + + indent + " count uint64\n" + + indent + " index uint64\n" + + indent + " offset uint64\n" + + indent + " actingVersion uint64\n", + groupClassName); + + for (int i = 0, size = fields.size(); i < size; i++) + { + final Token signalToken = fields.get(i); + if (signalToken.signal() == Signal.BEGIN_FIELD) + { + final Token encodingToken = fields.get(i + 1); + final String propertyName = formatPropertyName(signalToken.name()); + final String typeName = formatClassName(encodingToken.applicableTypeName()); + + switch (encodingToken.signal()) + { + case BEGIN_SET: + new Formatter(sb).format(indent + " _%1$s %2$s\n", + propertyName, + typeName); + break; + + case BEGIN_COMPOSITE: + new Formatter(sb).format(indent + " _%1$s %2$s\n", + propertyName, + typeName); + break; + + default: + break; + } + } + } + + for (int i = 0, size = groups.size(); i < size; ) + { + final Token token = groups.get(i); + if (token.signal() == Signal.BEGIN_GROUP) + { + final String propertyName = formatPropertyName(token.name()); + final String groupName = groupClassName + formatClassName(token.name()); + + new Formatter(sb).format(indent + " _%1$s %2$s\n", + propertyName, + groupName); + i += token.componentTokenCount(); + } + } + + new Formatter(sb).format( + indent + "}\n\n" + + indent + "func (g *%1$s) sbePositionPtr() *uint64 {\n" + + indent + " return g.positionPtr\n" + + indent + "}\n\n", + groupClassName); + + new Formatter(sb).format( + indent + "func (g *%3$s) WrapForDecode(\n" + + indent + " buffer []byte,\n" + + indent + " pos *uint64,\n" + + indent + " actingVersion uint64,\n" + + indent + " bufferLength uint64) {\n" + + indent + " dimensions := %2$s{}\n" + + indent + " dimensions.Wrap(buffer, *pos, actingVersion, bufferLength)\n" + + indent + " g.buffer = buffer\n" + + indent + " g.bufferLength = bufferLength\n" + + indent + " g.blockLength = uint64(dimensions.BlockLength())\n" + + indent + " g.count = uint64(dimensions.NumInGroup())\n" + + indent + " g.index = 0\n" + + indent + " g.actingVersion = actingVersion\n" + + indent + " g.initialPosition = *pos\n" + + indent + " g.positionPtr = pos\n" + + indent + " *g.positionPtr = *g.positionPtr + %1$d\n" + + indent + "}\n", + dimensionHeaderLength, dimensionsClassName, groupClassName); + + final long minCount = numInGroupToken.encoding().applicableMinValue().longValue(); + final String minCheck = minCount > 0 ? "count < " + minCount + " || " : ""; + + new Formatter(sb).format("\n" + + indent + "func (g *%8$s) WrapForEncode(\n" + + indent + " buffer []byte,\n" + + indent + " count %3$s,\n" + + indent + " pos *uint64,\n" + + indent + " actingVersion uint64,\n" + + indent + " bufferLength uint64) {\n" + + indent + " if %5$scount > %6$d {\n" + + indent + " panic(\"count outside of allowed range [E110]\")\n" + + indent + " }\n" + + indent + " g.buffer = buffer\n" + + indent + " g.bufferLength = bufferLength\n" + + indent + " dimensions := %7$s{}\n" + + indent + " dimensions.Wrap(buffer, *pos, actingVersion, bufferLength)\n" + + indent + " dimensions.SetBlockLength(%2$d)\n" + + indent + " dimensions.SetNumInGroup(count)\n" + + indent + " g.index = 0\n" + + indent + " g.count = uint64(count)\n" + + indent + " g.blockLength = %2$d\n" + + indent + " g.actingVersion = actingVersion\n" + + indent + " g.initialPosition = *pos\n" + + indent + " g.positionPtr = pos\n" + + indent + " *g.positionPtr = *g.positionPtr + %4$d\n" + + indent + "}\n", + golangTypeBlockLength, + blockLength, + golangTypeNumInGroup, + dimensionHeaderLength, + minCheck, + numInGroupToken.encoding().applicableMaxValue().longValue(), + dimensionsClassName, + groupClassName); + + new Formatter(sb).format("\n" + + indent + "func (g *%3$s) SbeHeaderSize() uint64 {\n" + + indent + " return %1$d\n" + + indent + "}\n\n" + + + indent + "func (g *%3$s) SbeBlockLength() uint64 {\n" + + indent + " return %2$d\n" + + indent + "}\n\n" + + + indent + "func (g *%3$s) SbePosition() uint64 {\n" + + indent + " return *g.positionPtr\n" + + indent + "}\n\n" + + + indent + "func (g *%3$s) SbeCheckPosition(position uint64) uint64 {\n" + + indent + " if position > g.bufferLength {\n" + + indent + " panic(\"buffer too short [E100]\")\n" + + indent + " }\n" + + indent + " return position\n" + + indent + "}\n\n" + + + indent + "func (g *%3$s) SetSbePosition(position uint64) {\n" + + indent + " *g.positionPtr = g.SbeCheckPosition(position)\n" + + indent + "}\n\n" + + + indent + "func (g *%3$s) Count() uint64 {\n" + + indent + " return g.count\n" + + indent + "}\n\n" + + + indent + "func (g *%3$s) HasNext() bool {\n" + + indent + " return g.index < g.count\n" + + indent + "}\n\n" + + + indent + "func (g *%3$s) Next() *%3$s {\n" + + indent + " if g.index >= g.count {\n" + + indent + " panic(\"index >= count [E108]\")\n" + + indent + " }\n" + + indent + " g.offset = *g.positionPtr\n" + + indent + " if g.offset+g.blockLength > g.bufferLength {\n" + + indent + " panic(\"buffer too short for next group index [E108]\")\n" + + indent + " }\n" + + indent + " *g.positionPtr = g.offset + g.blockLength\n" + + indent + " g.index++\n\n" + + + indent + " return g\n" + + indent + "}\n", + dimensionHeaderLength, + blockLength, + groupClassName); + sb.append("\n") + .append(indent).append("func (g *").append(groupClassName) + .append(") ResetCountToIndex() uint64 {\n") + .append(indent).append(" g.count = g.index\n") + .append(indent).append(" dimensions := ").append(dimensionsClassName).append("{}\n") + .append(indent).append(" dimensions.Wrap") + .append("(g.buffer, g.initialPosition, g.actingVersion, g.bufferLength)\n") + .append(indent) + .append(" dimensions.SetNumInGroup(").append(golangTypeNumInGroup).append("(g.count))\n") + .append(indent).append(" return g.count\n") + .append(indent).append("}\n\n"); + + sb.append("\n") + .append(indent).append("func (g *").append(groupClassName) + .append(") ForEach(fn func(group *").append(groupClassName).append(")) {\n") + .append(indent).append(" for g.HasNext() {\n") + .append(indent).append(" g.Next()\n") + .append(indent).append(" fn(g)\n") + .append(indent).append(" }\n") + .append(indent).append("}\n\n"); + } + + private static void generateGroupProperty( + final StringBuilder sb, + final String groupName, + final String groupClassName, + final String outerClassName, + final Token token, + final String goTypeForNumInGroup, + final String indent) + { + final String propertyName = formatPropertyName(groupName); + + new Formatter(sb).format( + indent + " func (m *%3$s) %1$sId() int {\n" + + indent + " return %2$d\n" + + indent + " }\n", + propertyName, + token.id(), + outerClassName); + + + new Formatter(sb).format("\n" + + indent + "func (m *%1$s) %2$s() *%3$s {\n" + + indent + " m._%2$s.WrapForDecode(m.buffer, m.sbePositionPtr(), m.actingVersion, m.bufferLength)\n" + + indent + " return &m._%2$s\n" + + indent + "}\n", + outerClassName, + propertyName, + groupClassName); + + new Formatter(sb).format("\n" + + indent + "func (m *%1$s) %2$sCount(count %3$s) *%4$s {\n" + + indent + + " m._%2$s.WrapForEncode(m.buffer, count, m.sbePositionPtr(), m.actingVersion, m.bufferLength)\n" + + indent + " return &m._%2$s\n" + + indent + "}\n", + outerClassName, + propertyName, + goTypeForNumInGroup, + groupClassName); + + final int version = token.version(); + final String versionCheck = 0 == version ? + " return true\n" : " return m.actingVersion >= m.%1$sSinceVersion()\n"; + new Formatter(sb).format("\n" + + indent + "func (m *%3$s) %1$sSinceVersion() uint64 {\n" + + indent + " return %2$d\n" + + indent + "}\n\n" + + + indent + "func (m *%3$s) %1$sInActingVersion() bool {\n" + + indent + versionCheck + + indent + "}\n", + propertyName, + version, + outerClassName); + } + + private static CharSequence generateChoiceNotPresentCondition(final int sinceVersion) + { + if (0 == sinceVersion) + { + return ""; + } + + return String.format( + " if m.actingVersion < %1$d {\n" + + " return false\n" + + " }\n\n", + sinceVersion); + } + + private static CharSequence generateArrayFieldNotPresentCondition( + final int sinceVersion, final String indent) + { + if (0 == sinceVersion) + { + return ""; + } + + return String.format( + indent + " if m.actingVersion < %1$d {\n" + + indent + " return 0\n" + + indent + " }\n\n", + sinceVersion); + } + + private static CharSequence generateArrayFieldNotPresentConditionWithErr( + final int sinceVersion, final String indent) + { + if (0 == sinceVersion) + { + return ""; + } + + return String.format( + indent + " if m.actingVersion < %1$d {\n" + + indent + " return 0, nil\n" + + indent + " }\n\n", + sinceVersion); + } + + private static CharSequence generateStringNotPresentCondition( + final int sinceVersion, final String indent) + { + if (0 == sinceVersion) + { + return ""; + } + + return String.format( + indent + " if m.actingVersion < %1$d {\n" + + indent + " return \"\"\n" + + indent + " }\n\n", + sinceVersion); + } + + private static CharSequence generateStringViewNotPresentCondition( + final int sinceVersion, final String indent) + { + if (0 == sinceVersion) + { + return ""; + } + + return String.format( + indent + " if m.actingVersion < %1$d {\n" + + indent + " return \"\"\n" + + indent + " }\n\n", + sinceVersion); + } + + private static CharSequence generateTypeFieldNotPresentCondition( + final int sinceVersion, final String indent) + { + if (0 == sinceVersion) + { + return ""; + } + + return String.format( + indent + " if m.actingVersion < %1$d {\n" + + indent + " return nil\n" + + indent + " }\n\n", + sinceVersion); + } + + private static CharSequence generateClassDeclaration(final String className) + { + return + "type " + className + " struct {\n"; + } + + private static CharSequence generateEnumDeclaration(final String name, final Token encodingToken) + { + final Encoding encoding = encodingToken.encoding(); + return "type " + name + " " + goTypeName(encoding.primitiveType()) + "\n"; + } + + private static CharSequence generateConstructorsAndOperators(final String className) + { + return String.format( + " func %1$s() {\n\n" + + + " func %1$s(\n" + + " buffer []byte,\n" + + " offset uint64,\n" + + " bufferLength uint64,\n" + + " actingBlockLength uint64,\n" + + " actingVersion uint64) {\n" + + " m.buffer := buffer\n" + + " m.bufferLength := bufferLength\n" + + " m.offset := offset\n" + + " m.position := sbeCheckPosition(offset + actingBlockLength)\n" + + " m.actingBlockLength := actingBlockLength\n" + + " m.actingVersion := actingVersion\n" + + " }\n\n", + className); + } + + private static void generateFieldMetaAttributeMethod( + final StringBuilder sb, final Token token, final String indent, final String className) + { + final Encoding encoding = token.encoding(); + final String propertyName = toUpperFirstChar(token.name()); + final String epoch = encoding.epoch() == null ? "" : encoding.epoch(); + final String timeUnit = encoding.timeUnit() == null ? "" : encoding.timeUnit(); + final String semanticType = encoding.semanticType() == null ? "" : encoding.semanticType(); + + sb.append("\n") + .append(indent).append("func (m *").append(className) + .append(") ").append(propertyName).append("MetaAttribute(metaAttribute string) string {\n") + .append(indent).append(" switch metaAttribute {\n"); + + if (!Strings.isEmpty(epoch)) + { + sb.append(indent) + .append(" case \"EPOCH\": return \"").append(epoch).append("\"\n"); + } + + if (!Strings.isEmpty(timeUnit)) + { + sb.append(indent) + .append(" case \"TIME_UNIT\": return \"").append(timeUnit).append("\"\n"); + } + + if (!Strings.isEmpty(semanticType)) + { + sb.append(indent) + .append(" case \"SEMANTIC_TYPE\": return \"").append(semanticType) + .append("\"\n"); + } + + sb + .append(indent).append(" case \"PRESENCE\": return \"") + .append(encoding.presence().toString().toLowerCase()).append("\"\n") + .append(indent).append(" default: return \"\"\n") + .append(indent).append(" }\n") + .append(indent).append("}\n"); + } + + private static CharSequence generateEnumFieldNotPresentCondition( + final int sinceVersion, final String enumName, final String indent) + { + if (0 == sinceVersion) + { + return ""; + } + + return String.format( + indent + " if m.actingVersion < %1$d {\n" + + indent + " return %2$s_NULL_VALUE\n" + + indent + " }\n\n", + sinceVersion, + enumName); + } + + private static void generateBitsetProperty( + final StringBuilder sb, + final String propertyName, + final Token token, + final String indent, + final String className) + { + final String bitsetName = formatClassName(token.applicableTypeName()); + final int offset = token.offset(); + + new Formatter(sb).format( + indent + " func (m *%4$s) %2$s() *%1$s {\n" + + indent + " m._%2$s.Wrap(m.buffer, m.offset + %3$d, m.actingVersion, m.bufferLength)\n" + + indent + " return &m._%2$s\n" + + indent + " }\n", + bitsetName, + propertyName, + offset, + className); + + new Formatter(sb).format("\n" + + indent + " func (m *%3$s) %1$sEncodingLength() int {\n" + + indent + " return %2$d\n" + + indent + " }\n", + propertyName, + token.encoding().primitiveType().size(), + className); + } + + private static void generateCompositeProperty( + final StringBuilder sb, + final String propertyName, + final Token token, + final String indent, + final String className) + { + final String compositeName = formatClassName(token.applicableTypeName()); + + new Formatter(sb).format( + indent + " func (m *%4$s) %2$s() *%1$s {\n" + + indent + " m._%2$s.Wrap(m.buffer, m.offset + %3$d, m.actingVersion, m.bufferLength)\n" + + indent + " return &m._%2$s\n" + + indent + " }\n", + compositeName, + propertyName, + token.offset(), + className); + } + + /** + * Generate the composites for dealing with the message header. + * + * @throws IOException if an error is encountered when writing the output. + */ + public void generateMessageHeaderStub() throws IOException + { + generateComposite(ir.headerStructure().tokens()); + } + + private List generateTypeStubs() throws IOException + { + final List typesToInclude = new ArrayList<>(); + + for (final List tokens : ir.types()) + { + switch (tokens.get(0).signal()) + { + case BEGIN_ENUM: + generateEnum(tokens); + break; + + case BEGIN_SET: + generateChoiceSet(tokens); + break; + + case BEGIN_COMPOSITE: + generateComposite(tokens); + break; + + default: + break; + } + + typesToInclude.add(tokens.get(0).applicableTypeName()); + } + + return typesToInclude; + } + + private List generateTypesToIncludes(final List tokens) + { + final List typesToInclude = new ArrayList<>(); + + for (final Token token : tokens) + { + switch (token.signal()) + { + case BEGIN_ENUM: + case BEGIN_SET: + case BEGIN_COMPOSITE: + typesToInclude.add(token.applicableTypeName()); + break; + + default: + break; + } + } + + return typesToInclude; + } + + /** + * {@inheritDoc} + */ + public void generate() throws IOException + { + + generateUtils(ir.namespaces()); + + generateMessageHeaderStub(); + final List typesToInclude = generateTypeStubs(); + + for (final List tokens : ir.messages()) + { + includes.clear(); + + final Token msgToken = tokens.get(0); + final String className = formatClassName(msgToken.name()); + + try (Writer fileOut = outputManager.createOutput(className)) + { + final StringBuilder out = new StringBuilder(); + out.append(generateClassDeclaration(className)); + + final List messageBody = tokens.subList(1, tokens.size() - 1); + int i = 0; + + final List fields = new ArrayList<>(); + i = collectFields(messageBody, i, fields); + + final List groups = new ArrayList<>(); + i = collectGroups(messageBody, i, groups); + + final List varData = new ArrayList<>(); + collectVarData(messageBody, i, varData); + + out.append(generateMessageFlyweightCode(className, msgToken, fields, groups)); + + final StringBuilder sb = new StringBuilder(); + generateFields(sb, className, fields, BASE_INDENT); + generateGroups(sb, groups, BASE_INDENT, className, className); + generateVarData(sb, className, varData, BASE_INDENT); + generateDisplay(sb, msgToken.name(), className, fields, groups, varData); + // TODO generate length + // sb.append(generateMessageLength(groups, varData, BASE_INDENT, className)); + out.append(sb); + + fileOut.append(generateFileHeader(ir.namespaces())); + fileOut.append(out); + } + } + } + + private void generateUtils(final String[] namespaces) throws IOException + { + + try (Writer out = outputManager.createOutput("Utils")) + { + out.append("/* Generated SBE (Simple Binary Encoding) message codec */\n"); + + out.append(String.format("// Code generated by SBE. DO NOT EDIT.\n\n" + + + "package %1$s\n\n" + + + "import (\n" + + "\t\"math\"\n" + + "\t\"math/bits\"\n" + + ")\n\n" + + + "// SbeBigEndianEncode16 encodes a 16-bit value into big endian byte order\n" + + "func SbeBigEndianEncode16(v uint16) uint16 {\n" + + "\treturn bits.ReverseBytes16(v)\n" + + "}\n\n" + + + "// SbeBigEndianEncode32 encodes a 32-bit value into big endian byte order\n" + + "func SbeBigEndianEncode32(v uint32) uint32 {\n" + + "\treturn bits.ReverseBytes32(v)\n" + + "}\n\n" + + + "// SbeBigEndianEncode64 encodes a 64-bit value into big endian byte order\n" + + "func SbeBigEndianEncode64(v uint64) uint64 {\n" + + "\treturn bits.ReverseBytes64(v)\n" + + "}\n\n" + + + "// SbeLittleEndianEncode16 encodes a 16-bit value into little endian byte order\n" + + "func SbeLittleEndianEncode16(v uint16) uint16 {\n" + + "\treturn v\n" + + "}\n\n" + + + "// SbeLittleEndianEncode32 encodes a 32-bit value into little endian byte order\n" + + "func SbeLittleEndianEncode32(v uint32) uint32 {\n" + + "\treturn v\n" + + "}\n\n" + + + "// SbeLittleEndianEncode64 encodes a 64-bit value into little endian byte order\n" + + "func SbeLittleEndianEncode64(v uint64) uint64 {\n" + + "\treturn v\n" + + "}\n\n" + + + "// SbeNullValueFloat returns the null value for a float\n" + + "func SbeNullValueFloat() float32 {\n" + + "\treturn float32(math.NaN())\n" + + "}\n\n" + + + "// SbeNullValueDouble returns the null value for a double\n" + + "func SbeNullValueDouble() float64 {\n" + + "\treturn math.NaN()\n" + + "}\n\n" + + + "// SbeNullValueByte returns the null value for a byte\n" + + "func SbeNullValueByte() byte {\n" + + "\treturn byte(math.MaxUint8)\n" + + "}\n\n" + + + "// SbeNullValueInt8 returns the null value for an 8-bit integer\n" + + "func SbeNullValueInt8() int8 {\n" + + "\treturn math.MinInt8\n" + + "}\n\n" + + + "// SbeNullValueInt16 returns the null value for a 16-bit integer\n" + + "func SbeNullValueInt16() int16 {\n" + + "\treturn math.MinInt16\n" + + "}\n\n" + + + "// SbeNullValueInt32 returns the null value for a 32-bit integer\n" + + "func SbeNullValueInt32() int32 {\n" + + "\treturn math.MinInt32\n" + + "}\n\n" + + + "// SbeNullValueInt64 returns the null value for a 64-bit integer\n" + + "func SbeNullValueInt64() int64 {\n" + + "\treturn math.MinInt64\n" + + "}\n\n" + + + "// SbeNullValueUint8 returns the null value for an 8-bit unsigned integer\n" + + "func SbeNullValueUint8() uint8 {\n" + + "\treturn math.MaxUint8\n" + + "}\n\n" + + + "// SbeNullValueUint16 returns the null value for a 16-bit unsigned integer\n" + + "func SbeNullValueUint16() uint16 {\n" + + "\treturn math.MaxUint16\n" + + "}\n\n" + + + "// SbeNullValueUint32 returns the null value for a 32-bit unsigned integer\n" + + "func SbeNullValueUint32() uint32 {\n" + + "\treturn math.MaxUint32\n" + + "}\n\n" + + + "// SbeNullValueUint64 returns the null value for a 64-bit unsigned integer\n" + + "func SbeNullValueUint64() uint64 {\n" + + "\treturn math.MaxUint64\n" + + "}\n\n" + + + "type MetaAttribute int\n" + + "const (\n" + + " EPOCH MetaAttribute = iota\n" + + " TIME_UNIT\n" + + " SEMANTIC_TYPE\n" + + " PRESENCE\n" + + ")\n\n" + + + "var SbeNoBoundsCheck = false\n\n" + + "// LittleEndian\n" + + "\n" + + "func ByteLittleEndian(b []byte) byte {\n" + + "\treturn b[0]\n" + + "}\n" + + "\n" + + "func PutByteLittleEndian(b []byte, v byte) {\n" + + "\tb[0] = v\n" + + "}\n" + + "\n" + + "func Uint8LittleEndian(b []byte) uint8 {\n" + + "\treturn b[0]\n" + + "}\n" + + "\n" + + "func PutUint8LittleEndian(b []byte, v uint8) {\n" + + "\tb[0] = v\n" + + "}\n" + + "\n" + + "func Int8LittleEndian(b []byte) int8 {\n" + + "\treturn int8(Uint8LittleEndian(b))\n" + + "}\n" + + "\n" + + "func PutInt8LittleEndian(b []byte, v int8) {\n" + + "\tPutUint8LittleEndian(b, uint8(v))\n" + + "}\n" + + "\n" + + "func Uint16LittleEndian(b []byte) uint16 {\n" + + "\t_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808\n" + + "\treturn uint16(b[0]) | uint16(b[1])<<8\n" + + "}\n" + + "\n" + + "func PutUint16LittleEndian(b []byte, v uint16) {\n" + + "\t_ = b[1] // early bounds check to guarantee safety of writes below\n" + + "\tb[0] = byte(v)\n" + + "\tb[1] = byte(v >> 8)\n" + + "}\n" + + "\n" + + "func Int16LittleEndian(b []byte) int16 {\n" + + "\treturn int16(Uint16LittleEndian(b))\n" + + "}\n" + + "\n" + + "func PutInt16LittleEndian(b []byte, v int16) {\n" + + "\tPutUint16LittleEndian(b, uint16(v))\n" + + "}\n" + + "\n" + + "func Uint32LittleEndian(b []byte) uint32 {\n" + + "\t_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808\n" + + "\treturn uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24\n" + + "}\n" + + "\n" + + "func PutUint32LittleEndian(b []byte, v uint32) {\n" + + "\t_ = b[3] // early bounds check to guarantee safety of writes below\n" + + "\tb[0] = byte(v)\n" + + "\tb[1] = byte(v >> 8)\n" + + "\tb[2] = byte(v >> 16)\n" + + "\tb[3] = byte(v >> 24)\n" + + "}\n" + + "\n" + + "func Int32LittleEndian(b []byte) int32 {\n" + + "\treturn int32(Uint32LittleEndian(b))\n" + + "}\n" + + "\n" + + "func PutInt32LittleEndian(b []byte, v int32) {\n" + + "\tPutUint32LittleEndian(b, uint32(v))\n" + + "}\n" + + "\n" + + "func Uint64LittleEndian(b []byte) uint64 {\n" + + "\t_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808\n" + + "\treturn uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |\n" + + "\t\tuint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56\n" + + "}\n" + + "\n" + + "func PutUint64LittleEndian(b []byte, v uint64) {\n" + + "\t_ = b[7] // early bounds check to guarantee safety of writes below\n" + + "\tb[0] = byte(v)\n" + + "\tb[1] = byte(v >> 8)\n" + + "\tb[2] = byte(v >> 16)\n" + + "\tb[3] = byte(v >> 24)\n" + + "\tb[4] = byte(v >> 32)\n" + + "\tb[5] = byte(v >> 40)\n" + + "\tb[6] = byte(v >> 48)\n" + + "\tb[7] = byte(v >> 56)\n" + + "}\n" + + "\n" + + "func Int64LittleEndian(b []byte) int64 {\n" + + "\treturn int64(Uint64LittleEndian(b))\n" + + "}\n" + + "\n" + + "func PutInt64LittleEndian(b []byte, v int64) {\n" + + "\tPutUint64LittleEndian(b, uint64(v))\n" + + "}\n" + + "\n" + + "func FloatLittleEndian(b []byte) float32 {\n" + + "\treturn math.Float32frombits(Uint32LittleEndian(b))\n" + + "}\n" + + "\n" + + "func PutFloatLittleEndian(b []byte, v float32) {\n" + + "\tPutUint32LittleEndian(b, math.Float32bits(v))\n" + + "}\n" + + "\n" + + "func DoubleLittleEndian(b []byte) float64 {\n" + + "\treturn math.Float64frombits(Uint64LittleEndian(b))\n" + + "}\n" + + "\n" + + "func PutDoubleLittleEndian(b []byte, v float64) {\n" + + "\tPutUint64LittleEndian(b, math.Float64bits(v))\n" + + "}\n" + + "\n" + + "// BigEndian\n" + + "\n" + + "func ByteBigEndian(b []byte) byte {\n" + + "\treturn b[0]\n" + + "}\n" + + "\n" + + "func PutByteBigEndian(b []byte, v byte) {\n" + + "\tb[0] = v\n" + + "}\n" + + "\n" + + "func Uint8BigEndian(b []byte) uint8 {\n" + + "\treturn b[0]\n" + + "}\n" + + "\n" + + "func PutUint8BigEndian(b []byte, v uint8) {\n" + + "\tb[0] = byte(v)\n" + + "}\n" + + "\n" + + "func Int8BigEndian(b []byte) int8 {\n" + + "\treturn int8(Uint8BigEndian(b))\n" + + "}\n" + + "\n" + + "func PutInt8BigEndian(b []byte, v int8) {\n" + + "\tPutUint8BigEndian(b, uint8(v))\n" + + "}\n" + + "\n" + + "func Uint16BigEndian(b []byte) uint16 {\n" + + "\t_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808\n" + + "\treturn uint16(b[1]) | uint16(b[0])<<8\n" + + "}\n" + + "\n" + + "func PutUint16BigEndian(b []byte, v uint16) {\n" + + "\t_ = b[1] // early bounds check to guarantee safety of writes below\n" + + "\tb[0] = byte(v >> 8)\n" + + "\tb[1] = byte(v)\n" + + "}\n" + + "\n" + + "func Int16BigEndian(b []byte) int16 {\n" + + "\treturn int16(Uint16BigEndian(b))\n" + + "}\n" + + "\n" + + "func PutInt16BigEndian(b []byte, v int16) {\n" + + "\tPutUint16BigEndian(b, uint16(v))\n" + + "}\n" + + "\n" + + "func Uint32BigEndian(b []byte) uint32 {\n" + + "\t_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808\n" + + "\treturn uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24\n" + + "}\n" + + "\n" + + "func PutUint32BigEndian(b []byte, v uint32) {\n" + + "\t_ = b[3] // early bounds check to guarantee safety of writes below\n" + + "\tb[0] = byte(v >> 24)\n" + + "\tb[1] = byte(v >> 16)\n" + + "\tb[2] = byte(v >> 8)\n" + + "\tb[3] = byte(v)\n" + + "}\n" + + "\n" + + "func Int32BigEndian(b []byte) int32 {\n" + + "\treturn int32(Uint32BigEndian(b))\n" + + "}\n" + + "\n" + + "func PutInt32BigEndian(b []byte, v int32) {\n" + + "\tPutUint32BigEndian(b, uint32(v))\n" + + "}\n" + + "\n" + + "func Uint64BigEndian(b []byte) uint64 {\n" + + "\t_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808\n" + + "\treturn uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |\n" + + "\t\tuint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56\n" + + "}\n" + + "\n" + + "func PutUint64BigEndian(b []byte, v uint64) {\n" + + "\t_ = b[7] // early bounds check to guarantee safety of writes below\n" + + "\tb[0] = byte(v >> 56)\n" + + "\tb[1] = byte(v >> 48)\n" + + "\tb[2] = byte(v >> 40)\n" + + "\tb[3] = byte(v >> 32)\n" + + "\tb[4] = byte(v >> 24)\n" + + "\tb[5] = byte(v >> 16)\n" + + "\tb[6] = byte(v >> 8)\n" + + "\tb[7] = byte(v)\n" + + "}\n" + + "\n" + + "func Int64BigEndian(b []byte) int64 {\n" + + "\treturn int64(Uint64BigEndian(b))\n" + + "}\n" + + "\n" + + "func PutInt64BigEndian(b []byte, v int64) {\n" + + "\tPutUint64BigEndian(b, uint64(v))\n" + + "}\n" + + "\n" + + "func FloatBigEndian(b []byte) float32 {\n" + + "\treturn math.Float32frombits(Uint32BigEndian(b))\n" + + "}\n" + + "\n" + + "func PutFloatBigEndian(b []byte, v float32) {\n" + + "\tPutUint32BigEndian(b, math.Float32bits(v))\n" + + "}\n" + + "\n" + + "func DoubleBigEndian(b []byte) float64 {\n" + + "\treturn math.Float64frombits(Uint64BigEndian(b))\n" + + "}\n" + + "\n" + + "func PutDoubleBigEndian(b []byte, v float64) {\n" + + "\tPutUint64BigEndian(b, math.Float64bits(v))\n" + + "}\n", + + namespacesToPackageName(namespaces))); + } + } + + private String formatIncludes(final Set includes) + { + final StringBuilder sb = new StringBuilder(); + + boolean first = true; + for (final String s : includes) + { + if (!first) + { + sb.append("\n"); + } + sb.append('"').append(s).append('"'); + first = false; + } + return sb.toString(); + } + + private void generateGroups( + final StringBuilder sb, + final List tokens, + final String indent, + final String outerClassName, + final String className) + { + for (int i = 0, size = tokens.size(); i < size; i++) + { + final Token groupToken = tokens.get(i); + if (groupToken.signal() != Signal.BEGIN_GROUP) + { + throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken); + } + + final String groupName = groupToken.name(); + final String groupClassName = formatClassName(outerClassName + formatClassName(groupToken.name())); + final Token numInGroupToken = Generators.findFirst("numInGroup", tokens, i); + final String goTypeForNumInGroup = goTypeName(numInGroupToken.encoding().primitiveType()); + + final int groupStart = i; + + ++i; + final int groupHeaderTokenCount = tokens.get(i).componentTokenCount(); + i += groupHeaderTokenCount; + + final List fields = new ArrayList<>(); + i = collectFields(tokens, i, fields); + + final List groups = new ArrayList<>(); + i = collectGroups(tokens, i, groups); + + generateGroupClassHeader(sb, groupClassName, tokens, groupStart, + indent + INDENT, fields, groups); + + generateFields(sb, groupClassName, fields, indent + INDENT); + generateGroups(sb, groups, indent + INDENT, groupClassName, className); + + final List varData = new ArrayList<>(); + i = collectVarData(tokens, i, varData); + generateVarData(sb, groupClassName, varData, indent + INDENT); + + sb.append(generateGroupDisplay(groupClassName, fields, groups, varData, indent + INDENT + INDENT)); + // TODO generate length + // sb.append(generateMessageLength(groups, varData, indent + INDENT + INDENT, groupClassName)); + + generateGroupProperty(sb, groupName, groupClassName, outerClassName, groupToken, goTypeForNumInGroup, + indent); + } + } + + private void generateVarData( + final StringBuilder sb, + final String className, + final List tokens, + final String indent) + { + for (int i = 0, size = tokens.size(); i < size; ) + { + final Token token = tokens.get(i); + if (token.signal() != Signal.BEGIN_VAR_DATA) + { + throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + token); + } + + final String propertyName = toUpperFirstChar(token.name()); + final Token lengthToken = Generators.findFirst("length", tokens, i); + final Token varDataToken = Generators.findFirst("varData", tokens, i); + final String characterEncoding = varDataToken.encoding().characterEncoding(); + final int lengthOfLengthField = lengthToken.encodedLength(); + final String lengthGoType = goTypeName(lengthToken.encoding().primitiveType()); + final String lengthByteOrderReadStr = formatReadBytes( + lengthToken.encoding().byteOrder(), lengthToken.encoding().primitiveType()); + + generateFieldMetaAttributeMethod(sb, token, indent, className); + + generateVarDataDescriptors( + sb, token, propertyName, characterEncoding, lengthToken, + lengthOfLengthField, lengthGoType, indent, className); + + new Formatter(sb).format("\n" + + indent + " func (m *%5$s) Skip%1$s() uint64 {\n" + + "%2$s" + + indent + " lengthOfLengthField := uint64(%3$d)\n" + + indent + " lengthPosition := m.SbePosition()\n" + + indent + + " dataLength := uint64(%4$s(m.buffer[lengthPosition : lengthPosition+lengthOfLengthField]))\n" + + indent + " m.SetSbePosition(lengthPosition + lengthOfLengthField + dataLength)\n" + + indent + " return dataLength\n" + + indent + " }\n", + propertyName, + generateArrayFieldNotPresentCondition(token.version(), indent), + lengthOfLengthField, + lengthByteOrderReadStr, + className); + + + new Formatter(sb).format("\n" + + indent + "func (m *%6$s) %1$s() string {\n" + + "%2$s" + + indent + " lengthFieldValue := uint64(%4$s(m.buffer[m.SbePosition():m.SbePosition()+%3$d]))\n" + + indent + " m.SetSbePosition(m.SbePosition() + uint64(%3$d))\n" + + indent + " pos := m.SbePosition()\n" + + indent + " m.SetSbePosition(pos + lengthFieldValue)\n" + + indent + " return string(m.buffer[pos:pos+lengthFieldValue])\n" + + indent + "}\n", + formatPropertyName(propertyName), + generateTypeFieldNotPresentCondition(token.version(), indent), + lengthOfLengthField, + lengthByteOrderReadStr, + lengthGoType, + className); + + new Formatter(sb).format("\n" + + indent + "func (m *%5$s) Get%1$s(dst []byte) int {\n" + + "%2$s" + + indent + " lengthOfLengthField := uint64(%3$d)\n" + + indent + " lengthPosition := m.SbePosition()\n" + + indent + " m.SetSbePosition(m.SbePosition() + lengthOfLengthField)\n" + + indent + + " dataLength := uint64(%4$s(m.buffer[lengthPosition:lengthPosition+lengthOfLengthField]))\n" + + indent + " bytesToCopy := dataLength\n" + + indent + " if uint64(len(dst)) < dataLength {\n" + + indent + " bytesToCopy = uint64(len(dst))\n" + + indent + " }\n" + + indent + " pos := m.SbePosition()\n" + + indent + " m.SetSbePosition(pos + dataLength)\n" + + indent + " copy(dst, m.buffer[pos:pos+bytesToCopy])\n" + + indent + " return int(bytesToCopy)\n" + + indent + "}\n", + propertyName, + generateArrayFieldNotPresentCondition(token.version(), indent), + lengthOfLengthField, + lengthByteOrderReadStr, + className); + + final String lengthByteOrderWriteStr = formatWriteBytes( + lengthToken.encoding().byteOrder(), lengthToken.encoding().primitiveType()); + new Formatter(sb).format("\n" + + indent + "func (m *%5$s) Put%1$sLen(src string, length %3$s) *%5$s {\n" + + indent + " lengthOfLengthField := uint64(%2$d)\n" + + indent + " lengthPosition := m.SbePosition()\n" + + indent + " m.SetSbePosition(lengthPosition + lengthOfLengthField)\n" + + indent + " %4$s(m.buffer[lengthPosition:], length)\n" + + indent + " if length != %3$s(0) {\n" + + indent + " pos := m.SbePosition()\n" + + indent + " m.SetSbePosition(pos + uint64(length))\n" + + indent + " copy(m.buffer[pos:], src)\n" + + indent + " }\n" + + indent + " return m\n" + + indent + "}\n", + propertyName, + lengthOfLengthField, + lengthGoType, + lengthByteOrderWriteStr, + className, + lengthByteOrderReadStr); + + new Formatter(sb).format("\n" + + indent + "func (m *%6$s) Get%1$sAsString() string {\n" + + "%2$s" + + indent + " lengthOfLengthField := uint64(%3$d)\n" + + indent + " lengthPosition := m.SbePosition()\n" + + indent + + " dataLength := uint64(%4$s(m.buffer[lengthPosition:lengthPosition+lengthOfLengthField]))\n" + + indent + " m.SetSbePosition(lengthPosition + lengthOfLengthField)\n" + + indent + " pos := m.SbePosition()\n" + + indent + " m.SetSbePosition(pos + dataLength)\n" + + indent + " return string(m.buffer[pos:pos+dataLength])\n" + + indent + "}\n", + propertyName, + generateStringNotPresentCondition(token.version(), indent), + lengthOfLengthField, + lengthByteOrderReadStr, + lengthGoType, + className); + + generateJsonEscapedStringGetter(sb, token, indent, propertyName, className); + + new Formatter(sb).format("\n" + + indent + "func (m *%1$s) Put%2$s(str string) *%1$s {\n" + + indent + " if len(str) > %4$d {\n" + + indent + " panic(\"string too long for length type [E109]\")\n" + + indent + " }\n" + + indent + " return m.Put%2$sLen(str, %3$s(len(str)))\n" + + indent + "}\n", + className, + propertyName, + lengthGoType, + lengthToken.encoding().applicableMaxValue().longValue()); + + i += token.componentTokenCount(); + } + } + + private void generateVarDataDescriptors( + final StringBuilder sb, + final Token token, + final String propertyName, + final String characterEncoding, + final Token lengthToken, + final Integer sizeOfLengthField, + final String lengthGoType, + final String indent, + final String className) + { + new Formatter(sb).format("\n" + + indent + "func (m *%3$s) %1$sCharacterEncoding() string {\n" + + indent + " return \"%2$s\"\n" + + indent + " }\n", + formatPropertyName(propertyName), + characterEncoding, + className); + + final int version = token.version(); + final String versionCheck = 0 == version ? + " return true\n" : " return m.actingVersion >= m.%1$sSinceVersion()\n"; + new Formatter(sb).format("\n" + + indent + "func (m *%4$s) %1$sSinceVersion() uint64 {\n" + + indent + " return %2$d\n" + + indent + " }\n\n" + + + indent + "func (m *%4$s) %1$sInActingVersion() bool {\n" + + indent + versionCheck + + indent + " }\n\n" + + + indent + "func (m *%4$s) %1$sId() uint16 {\n" + + indent + " return %3$d\n" + + indent + " }\n", + formatPropertyName(propertyName), + version, + token.id(), + className); + + + new Formatter(sb).format("\n" + + indent + "func (m *%3$s) %1$sHeaderLength() uint64 {\n" + + indent + " return %2$d\n" + + indent + "}\n", + formatPropertyName(propertyName), + sizeOfLengthField, + className); + + new Formatter(sb).format("\n" + + indent + "func (m *%5$s) %1$sLength() %4$s {\n" + + "%2$s" + + indent + " return %3$s(m.buffer[m.SbePosition():])\n" + + indent + "}\n", + formatPropertyName(propertyName), + generateArrayFieldNotPresentCondition(version, BASE_INDENT), + formatReadBytes(lengthToken.encoding().byteOrder(), lengthToken.encoding().primitiveType()), + lengthGoType, + className); + } + + private void generateChoiceSet(final List tokens) throws IOException + { + final Token token = tokens.get(0); + final String bitSetName = formatClassName(token.applicableTypeName()); + + try (Writer fileOut = outputManager.createOutput(bitSetName)) + { + includes.clear(); + final StringBuilder out = new StringBuilder(); + + out.append(generateClassDeclaration(bitSetName)); + out.append(generateFixedFlyweightCode(bitSetName, token.encodedLength(), tokens)); + + final Encoding encoding = token.encoding(); + new Formatter(out).format("\n" + + " func (bs *%1$s) Clear() *%1$s {\n" + + " %2$s(bs.buffer[bs.offset:], 0)\n" + + " return bs\n" + + " }\n", + bitSetName, + formatWriteBytes(token.encoding().byteOrder(), encoding.primitiveType())); + + new Formatter(out).format("\n" + + " func (bs *%1$s) IsEmpty() bool {\n" + + " return %2$s(bs.buffer[bs.offset:]) == 0\n" + + " }\n", + bitSetName, + formatReadBytes(token.encoding().byteOrder(), encoding.primitiveType())); + + new Formatter(out).format("\n" + + " func (bs *%1$s) RawValue() %2$s {\n" + + " return %2$s(bs.buffer[bs.offset])\n" + + " }\n", + bitSetName, + goTypeName(encoding.primitiveType())); + + out.append(generateChoices(bitSetName, tokens.subList(1, tokens.size() - 1))); + out.append(generateChoicesDisplay(bitSetName, tokens.subList(1, tokens.size() - 1))); + out.append("\n"); + + fileOut.append(generateFileHeader(ir.namespaces())); + fileOut.append(out); + } + } + + private void generateEnum(final List tokens) throws IOException + { + final Token enumToken = tokens.get(0); + final String enumName = formatClassName(tokens.get(0).applicableTypeName()); + + try (Writer fileOut = outputManager.createOutput(enumName)) + { + includes.clear(); + final StringBuilder out = new StringBuilder(); + + out.append(generateEnumDeclaration(enumName, enumToken)); + + out.append(generateEnumValues(tokens.subList(1, tokens.size() - 1), enumName, enumToken)); + +// out.append(generateEnumLookupMethod(tokens.subList(1, tokens.size() - 1), enumToken)); + + out.append(generateEnumDisplay(tokens.subList(1, tokens.size() - 1), enumToken)); + + out.append("\n"); + + fileOut.append(generateEnumFileHeader(ir.namespaces(), enumName)); + fileOut.append(out); + } + } + + private void generateComposite(final List tokens) throws IOException + { + final String compositeName = formatClassName(tokens.get(0).applicableTypeName()); + + try (Writer fileOut = outputManager.createOutput(compositeName)) + { + includes.clear(); + final StringBuilder out = new StringBuilder(); + + out.append(generateClassDeclaration(compositeName)); + out.append(generateFixedFlyweightCode(compositeName, tokens.get(0).encodedLength(), tokens)); + + out.append(generateCompositePropertyElements( + compositeName, tokens.subList(1, tokens.size() - 1), BASE_INDENT)); + + out.append(generateCompositeDisplay( + tokens.get(0).applicableTypeName(), tokens.subList(1, tokens.size() - 1))); + + fileOut.append(generateFileHeader(ir.namespaces())); + fileOut.append(out); + } + } + + private CharSequence generateChoices(final String bitsetClassName, final List tokens) + { + final StringBuilder sb = new StringBuilder(); + + tokens + .stream() + .filter((token) -> token.signal() == Signal.CHOICE) + .forEach((token) -> + { + final String choiceName = formatPropertyName(token.name()); + final PrimitiveType type = token.encoding().primitiveType(); + final String typeName = goTypeName(type); + final String choiceBitPosition = token.encoding().constValue().toString(); + final CharSequence constantOne = generateLiteral(type, "1"); + + new Formatter(sb).format(" type %1$s %2$s\n", + choiceName, + typeName); + + new Formatter(sb).format("\n" + + " func (m *%5$s) %1$sValue(bits %2$s) bool {\n" + + " return (bits & (%4$s << %3$s)) != 0\n" + + " }\n", + choiceName, + typeName, + choiceBitPosition, + constantOne, + bitsetClassName); + + new Formatter(sb).format("\n" + + " func (m *%5$s) Set%1$sValue(bits %2$s, value bool) %2$s {\n" + + " if value {\n" + + " return bits | (%4$s << %3$s)\n" + + " }\n" + + " return bits & ^(%4$s << %3$s)\n" + + " }\n", + choiceName, + typeName, + choiceBitPosition, + constantOne, + bitsetClassName); + + + new Formatter(sb).format("\n" + + "func (m *%1$s) %2$s() bool {\n" + + "%3$s" + + " return m.%2$sValue(%4$s(m.buffer[m.offset:]))\n" + + "}\n", + bitsetClassName, + choiceName, + generateChoiceNotPresentCondition(token.version()), + formatReadBytes(token.encoding().byteOrder(), type), + typeName, + choiceBitPosition); + + new Formatter(sb).format("\n" + + "func (m *%1$s) Set%2$s(value bool) *%1$s{\n" + + " bits := %7$s(m.buffer[m.offset:])\n" + + " if value {\n" + + " bits = %3$s(%3$s(bits) | (%6$s << %5$s))\n" + + " } else {\n" + + " bits = %3$s(%3$s(bits) & ^(%6$s << %5$s))\n" + + " }\n" + + " %4$s(m.buffer[m.offset:], bits)\n" + + " return m\n" + + "}\n", + bitsetClassName, + choiceName, + typeName, + formatWriteBytes(token.encoding().byteOrder(), type), + choiceBitPosition, + constantOne, + formatReadBytes(token.encoding().byteOrder(), type)); + }); + + return sb; + } + + private CharSequence generateEnumValues( + final List tokens, + final String enumName, + final Token encodingToken) + { + final StringBuilder sb = new StringBuilder(); + final Encoding encoding = encodingToken.encoding(); + + sb.append(" const (\n"); + + for (final Token token : tokens) + { + final CharSequence constVal = generateLiteral( + token.encoding().primitiveType(), token.encoding().constValue().toString()); + sb.append(" ").append(enumName).append("_").append(token.name()).append(" ").append(enumName) + .append(" = ").append(enumName).append("(").append(constVal).append(")\n"); + } + + final CharSequence nullLiteral = generateLiteral( + encoding.primitiveType(), encoding.applicableNullValue().toString()); + if (shouldDecodeUnknownEnumValues) + { + sb.append(" ").append(enumName).append("_").append("UNKNOWN") + .append(" ").append(enumName).append(" = ").append(enumName) + .append("(").append(nullLiteral).append(")\n"); + } + + sb.append(" ").append(enumName).append("_").append("NULL_VALUE") + .append(" ").append(enumName) + .append(" = ").append(enumName).append("(") + .append(nullLiteral).append(")\n"); + sb.append("\n )\n\n"); + + return sb; + } + + private CharSequence generateFieldNotPresentCondition( + final int sinceVersion, final Encoding encoding, final String indent) + { + if (0 == sinceVersion) + { + return ""; + } + + return String.format( + indent + " if m.actingVersion < %1$d {\n" + + indent + " return %2$s\n" + + indent + " }\n\n", + sinceVersion, + generateLiteral(encoding.primitiveType(), encoding.applicableNullValue().toString())); + } + + + private String namespacesToPackageName(final CharSequence[] namespaces) + { + return String.join("_", namespaces).toLowerCase().replace('.', '_').replace(' ', '_').replace('-', '_'); + } + + private CharSequence generateFileHeader(final CharSequence[] namespaces) + { + final StringBuilder sb = new StringBuilder(); + + sb.append("/* Generated SBE (Simple Binary Encoding) message codec */\n"); + + + sb.append(String.format( + "// Code generated by SBE. DO NOT EDIT.\n\n" + + + "package %1$s\n\n" + + + "import (\n" + + "%2$s\n" + + ")\n\n", + namespacesToPackageName(namespaces), + formatIncludes(includes))); + + return sb; + } + + private CharSequence generateEnumFileHeader( + final CharSequence[] namespaces, + final String className) + { + final StringBuilder sb = new StringBuilder(); + + sb.append("/* Generated SBE (Simple Binary Encoding) message codec */\n"); + + sb.append(String.format( + "// Code generated by SBE. DO NOT EDIT.\n\n" + + + "package %1$s\n\n" + + + "import (\n" + + "%2$s\n" + + ")\n\n", + namespacesToPackageName(namespaces), + formatIncludes(includes))); + + return sb; + } + + private CharSequence generateCompositePropertyElements( + final String containingClassName, final List tokens, final String indent) + { + final StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < tokens.size(); ) + { + final Token fieldToken = tokens.get(i); + final String propertyName = formatPropertyName(fieldToken.name()); + + generateFieldMetaAttributeMethod(sb, fieldToken, indent, containingClassName); + generateFieldCommonMethods(indent, sb, fieldToken, fieldToken, propertyName, containingClassName); + + switch (fieldToken.signal()) + { + case ENCODING: + generatePrimitiveProperty(sb, containingClassName, propertyName, fieldToken, fieldToken, indent); + break; + + case BEGIN_ENUM: + generateEnumProperty(sb, containingClassName, fieldToken, propertyName, fieldToken, indent); + break; + + case BEGIN_SET: + generateBitsetProperty(sb, propertyName, fieldToken, indent, containingClassName); + break; + + case BEGIN_COMPOSITE: + generateCompositeProperty(sb, propertyName, fieldToken, indent, containingClassName); + break; + + default: + break; + } + + i += tokens.get(i).componentTokenCount(); + } + + return sb; + } + + private void generatePrimitiveProperty( + final StringBuilder sb, + final String containingClassName, + final String propertyName, + final Token propertyToken, + final Token encodingToken, + final String indent) + { + generatePrimitiveFieldMetaData(sb, propertyName, encodingToken, indent, containingClassName); + + if (encodingToken.isConstantEncoding()) + { + generateConstPropertyMethods(sb, containingClassName, propertyName, encodingToken, indent); + } + else + { + generatePrimitivePropertyMethods( + sb, containingClassName, propertyName, propertyToken, encodingToken, indent); + } + } + + private void generatePrimitivePropertyMethods( + final StringBuilder sb, + final String containingClassName, + final String propertyName, + final Token propertyToken, + final Token encodingToken, + final String indent) + { + final int arrayLength = encodingToken.arrayLength(); + if (arrayLength == 1) + { + generateSingleValueProperty(sb, containingClassName, propertyName, propertyToken, encodingToken, indent); + } + else if (arrayLength > 1) + { + generateArrayProperty(sb, containingClassName, propertyName, propertyToken, encodingToken, indent); + } + } + + private void generatePrimitiveFieldMetaData( + final StringBuilder sb, + final String propertyName, + final Token token, + final String indent, + final String className) + { + final Encoding encoding = token.encoding(); + final PrimitiveType primitiveType = encoding.primitiveType(); + final String goTypeName = goTypeName(primitiveType); + final CharSequence nullValueString = generateNullValueLiteral(primitiveType, encoding); + + new Formatter(sb).format("\n" + + indent + " func (m *%4$s) %1$sNullValue() %2$s {\n" + + indent + " return %3$s\n" + + indent + " }\n", + propertyName, + goTypeName, + nullValueString, + className); + + new Formatter(sb).format("\n" + + indent + " func (m *%4$s) %1$sMinValue() %2$s {\n" + + indent + " return %3$s\n" + + indent + " }\n", + propertyName, + goTypeName, + generateLiteral(primitiveType, token.encoding().applicableMinValue().toString()), + className); + + new Formatter(sb).format("\n" + + indent + " func (m *%4$s) %1$sMaxValue() %2$s {\n" + + indent + " return %3$s\n" + + indent + " }\n", + propertyName, + goTypeName, + generateLiteral(primitiveType, token.encoding().applicableMaxValue().toString()), + className); + + new Formatter(sb).format("\n" + + indent + " func (m *%3$s) %1$sEncodingLength() int {\n" + + indent + " return %2$d\n" + + indent + " }\n", + propertyName, + token.encoding().primitiveType().size() * token.arrayLength(), + className); + } + + private CharSequence generateLoadValue( + final PrimitiveType primitiveType, + final String offsetStr, + final ByteOrder byteOrder, + final String indent) + { + final String goTypeName = goTypeName(primitiveType); + final StringBuilder sb = new StringBuilder(); + + new Formatter(sb).format( + indent + " return %3$s(m.buffer[(m.offset + %1$s):])\n", + offsetStr, + goTypeName, + formatReadBytes(byteOrder, primitiveType)); + + return sb; + } + + private CharSequence generateStoreValue( + final PrimitiveType primitiveType, + final String valueSuffix, + final String offsetStr, + final ByteOrder byteOrder, + final String indent) + { + final String goTypeName = goTypeName(primitiveType); + final String goMarshalType = golangMarshalType(primitiveType); + final StringBuilder sb = new StringBuilder(); + + new Formatter(sb).format( + indent + " %3$s(m.buffer[(m.offset + %4$s):], value%2$s)\n", + goTypeName, + valueSuffix, + formatWriteBytes(byteOrder, primitiveType), + offsetStr, + goMarshalType); + + return sb; + } + + private void generateSingleValueProperty( + final StringBuilder sb, + final String containingClassName, + final String propertyName, + final Token propertyToken, + final Token encodingToken, + final String indent) + { + final PrimitiveType primitiveType = encodingToken.encoding().primitiveType(); + final String goTypeName = goTypeName(primitiveType); + final int offset = encodingToken.offset(); + + new Formatter(sb).format("\n" + + indent + " func (m *%1$s) %2$s() %3$s {\n" + + "%4$s" + + "%5$s" + + indent + " }\n", + containingClassName, + propertyName, + goTypeName, + generateFieldNotPresentCondition(propertyToken.version(), encodingToken.encoding(), indent), + generateLoadValue(primitiveType, Integer.toString(offset), encodingToken.encoding().byteOrder(), indent)); + + final CharSequence storeValue = generateStoreValue( + primitiveType, "", Integer.toString(offset), encodingToken.encoding().byteOrder(), indent); + + new Formatter(sb).format("\n" + + indent + " func (m *%1$s) Set%2$s(value %3$s) *%1$s {\n" + + "%4$s" + + indent + " return m\n" + + indent + " }\n", + containingClassName, + propertyName, + goTypeName, + storeValue); + } + + private void generateArrayProperty( + final StringBuilder sb, + final String containingClassName, + final String propertyName, + final Token propertyToken, + final Token encodingToken, + final String indent) + { + final PrimitiveType primitiveType = encodingToken.encoding().primitiveType(); + final String goTypeName = goTypeName(primitiveType); + final int offset = encodingToken.offset(); + + final int arrayLength = encodingToken.arrayLength(); + new Formatter(sb).format("\n" + + indent + " func (m *%3$s) %1$sLength() int {\n" + + indent + " return %2$d\n" + + indent + " }\n", + propertyName, + arrayLength, + formatClassName(containingClassName)); + + // TODO return slice from array +// new Formatter(sb).format("\n" + +// indent + " func (m *%5$s) %1$s() []%2$s {\n" + +// "%3$s" + +// indent + " return m.buffer[(m.offset + %4$d):]\n" + +// indent + " }\n", +// propertyName, +// goTypeName, +// generateTypeFieldNotPresentCondition(propertyToken.version(), indent), +// offset, +// formatClassName(containingClassName)); + + final CharSequence loadValue = generateLoadValue( + primitiveType, + String.format("%d + (index * %d)", offset, primitiveType.size()), + encodingToken.encoding().byteOrder(), + indent); + + + new Formatter(sb).format("\n" + + indent + " func (m *%6$s) %2$sIndex(index uint64) %1$s {\n" + + indent + " if (index >= %3$d) {\n" + + indent + " panic(\"index out of range for %2$s [E104]\")\n" + + indent + " }\n\n" + + "%4$s" + + "%5$s" + + indent + " }\n", + goTypeName, + propertyName, + arrayLength, + generateFieldNotPresentCondition(propertyToken.version(), encodingToken.encoding(), indent), + loadValue, + formatClassName(containingClassName)); + + final CharSequence storeValue = generateStoreValue( + primitiveType, + "", + String.format("%d + (index * %d)", offset, primitiveType.size()), + encodingToken.encoding().byteOrder(), + indent); + + new Formatter(sb).format("\n" + + indent + " func (m *%6$s) Set%2$sIndex(index uint64, value %3$s) *%6$s {\n" + + indent + " if (index >= %4$d) {\n" + + indent + " panic(\"index out of range for %2$s [E105]\")\n" + + indent + " }\n\n" + + + "%5$s" + + indent + " return m\n" + + indent + " }\n", + containingClassName, + propertyName, + goTypeName, + arrayLength, + storeValue, + formatClassName(containingClassName)); + + + addInclude("fmt"); + new Formatter(sb).format("\n" + + indent + "func (m *%6$s) Get%2$s(dst []byte) (uint64, error) {\n" + + indent + " if len(dst) > %3$d {\n" + + indent + " return 0, fmt.Errorf(\"length too large for get%2$s [E106]\")\n" + + indent + " }\n\n" + + + "%4$s" + + indent + " copy(dst, m.buffer[m.offset+%5$d:])\n" + + indent + " return %3$d, nil\n" + + indent + "}\n", + goTypeName, + toUpperFirstChar(propertyName), + arrayLength, + generateArrayFieldNotPresentConditionWithErr(propertyToken.version(), indent), + offset, + formatClassName(containingClassName)); + + new Formatter(sb).format("\n" + + indent + "func (m *%1$s) Put%2$s(src []byte) *%1$s {\n" + + indent + " copy(m.buffer[(m.offset+%3$d):], src)\n" + + indent + " return m\n" + + indent + "}\n", + formatClassName(containingClassName), + formatPropertyName(propertyName), + offset); + + if (arrayLength > 1 && arrayLength <= 4) + { + sb.append("\n").append(indent).append("func (m *") + .append(formatClassName(containingClassName)).append(") Put") + .append(formatPropertyName(propertyName)) + .append("Values (\n"); + + for (int i = 0; i < arrayLength; i++) + { + sb.append(indent).append(" value").append(i) + .append(" ") + .append(goTypeName); + + if (i < (arrayLength - 1)) + { + sb.append(",\n"); + } + } + + sb.append(")") + .append(" *") + .append(formatClassName(containingClassName)) + .append(" {\n"); + + for (int i = 0; i < arrayLength; i++) + { + sb.append(generateStoreValue( + primitiveType, + Integer.toString(i), + Integer.toString(offset + (i * primitiveType.size())), + encodingToken.encoding().byteOrder(), + indent)); + } + + sb.append("\n"); + sb.append(indent).append(" return m\n"); + sb.append(indent).append("}\n"); + } + + + if (encodingToken.encoding().primitiveType() == PrimitiveType.CHAR) + { + new Formatter(sb).format("\n" + + indent + " func (m *%4$s) Get%1$sAsString() string {\n" + + indent + " offset := m.offset + %2$d\n" + + indent + " length := uint64(0)\n\n" + + indent + " for length < %3$d && m.buffer[offset + length] != '\\x00' {\n" + + indent + " length++\n" + + indent + " }\n" + + indent + " return string(m.buffer[offset:offset + length])\n" + + indent + " }\n", + toUpperFirstChar(propertyName), + offset, + arrayLength, + formatClassName(containingClassName)); + + generateJsonEscapedStringGetter(sb, encodingToken, indent, propertyName, containingClassName); + + new Formatter(sb).format("\n" + + indent + "func (m *%1$s) Set%2$s(src string) *%1$s {\n" + + indent + " copy(m.buffer[(m.offset+%3$d):], []byte(src))\n" + + indent + " return m\n" + + indent + "}\n", + formatClassName(containingClassName), + formatPropertyName(propertyName), + offset); + } + } + + private void generateJsonEscapedStringGetter( + final StringBuilder sb, + final Token token, + final String indent, + final String propertyName, + final String className) + { + addInclude("strings"); + addInclude("fmt"); + new Formatter(sb).format("\n" + + indent + "func (m *%3$s) Get%1$sAsJsonEscapedString() string {\n" + + "%2$s" + + indent + " oss := strings.Builder{}\n" + + indent + " s := m.Get%1$sAsString()\n\n" + + indent + " for _, c := range s {\n" + + indent + " switch c {\n" + + indent + " case '\"': oss.WriteString(\"\\\\\\\"\")\n" + + indent + " case '\\\\': oss.WriteString(\"\\\\\\\\\")\n" + + indent + " case '\\b': oss.WriteString(\"\\\\b\")\n" + + indent + " case '\\f': oss.WriteString(\"\\\\f\")\n" + + indent + " case '\\n': oss.WriteString(\"\\\\n\")\n" + + indent + " case '\\r': oss.WriteString(\"\\\\r\")\n" + + indent + " case '\\t': oss.WriteString(\"\\\\t\")\n\n" + + indent + " default:\n" + + indent + " if '\\x00' <= c && c <= '\\x1f' {\n" + + indent + " fmt.Fprintf(&oss, \"%%x\", c)\n" + + indent + " } else {\n" + + indent + " oss.WriteString(string(c))\n" + + indent + " }\n" + + indent + " }\n" + + indent + " }\n\n" + + indent + " return oss.String()\n" + + indent + "}\n", + toUpperFirstChar(propertyName), + generateStringNotPresentCondition(token.version(), indent), + className); + } + + private void generateConstPropertyMethods( + final StringBuilder sb, + final String className, + final String propertyName, + final Token token, + final String indent) + { + final String goTypeName = goTypeName(token.encoding().primitiveType()); + + if (token.encoding().primitiveType() != PrimitiveType.CHAR) + { + new Formatter(sb).format("\n" + + indent + " func (m *%4$s) %2$s() %1$s {\n" + + indent + " return %3$s\n" + + indent + " }\n", + goTypeName, + propertyName, + generateLiteral(token.encoding().primitiveType(), token.encoding().constValue().toString()), + className); + + return; + } + + final byte[] constantValue = token.encoding().constValue().byteArrayValue(token.encoding().primitiveType()); + final StringBuilder values = new StringBuilder(); + for (final byte b : constantValue) + { + values.append(b).append(", "); + } + + if (values.length() > 0) + { + values.setLength(values.length() - 2); + } + + new Formatter(sb).format("\n" + + indent + " func (m *%3$s) %1$sLength() uint64 {\n" + + indent + " return %2$d\n" + + indent + " }\n", + propertyName, + constantValue.length, + className); + + new Formatter(sb).format("\n" + + indent + " func (m *%3$s) %1$s() string {\n" + + indent + " %1$sValues := []uint8{ %2$s, 0 }\n\n" + + indent + " return string(%1$sValues)\n" + + indent + " }\n", + propertyName, + values, + className); + + sb.append(String.format("\n" + + indent + " func (m *%4$s) %2$sIndex(index uint64) %1$s {\n" + + indent + " %2$sValues := []uint8{ %3$s, 0 }\n\n" + + indent + " return byte(%2$sValues[index])\n" + + indent + " }\n", + goTypeName, + propertyName, + values, + className)); + + new Formatter(sb).format("\n" + + indent + " func (m *%4$s) Get%1$s(dst []byte, length uint64) uint64 {\n" + + indent + " %2$sValues := []uint8{ %3$s }\n" + + indent + " bytesToCopy := uint64(len(%2$sValues))\n" + + indent + " if length < uint64(len(%2$sValues)) {\n" + + indent + " bytesToCopy = length\n" + + indent + " }\n" + + indent + " copy(dst, %2$sValues[:bytesToCopy])\n" + + indent + " return bytesToCopy\n" + + indent + " }\n", + toUpperFirstChar(propertyName), + propertyName, + values, + className); + + new Formatter(sb).format("\n" + + indent + " func (m *%3$s) Get%1$sAsString() string {\n" + + indent + " %1$sValues := []uint8{ %2$s, 0 }\n\n" + + indent + " return string(%1$sValues)\n" + + indent + " }\n", + propertyName, + values, + className); + + generateJsonEscapedStringGetter(sb, token, indent, propertyName, className); + } + + private CharSequence generateFixedFlyweightCode( + final String className, + final int size, + final List fields) + { + final String schemaIdType = goTypeName(ir.headerStructure().schemaIdType()); + final String schemaVersionType = goTypeName(ir.headerStructure().schemaVersionType()); + + final StringBuilder sb = new StringBuilder(); + for (int i = 1, fieldSize = fields.size(); i < fieldSize; ) + { + final Token signalToken = fields.get(i); + final String propertyName = formatPropertyName(signalToken.name()); + final String typeName = formatClassName(signalToken.applicableTypeName()); + + switch (signalToken.signal()) + { + case BEGIN_SET: + new Formatter(sb).format(" _%1$s %2$s\n", + propertyName, + typeName); + break; + + case BEGIN_COMPOSITE: + new Formatter(sb).format(" _%1$s %2$s\n", + propertyName, + typeName); + break; + + default: + break; + } + + i += fields.get(i).componentTokenCount(); + } + + String sizeValue = String.format("%1$d", size); + if (size == -1) + { + sizeValue = "SbeNullValueUint64()"; + } + + return String.format( + " buffer []byte\n" + + " bufferLength uint64\n" + + " offset uint64\n" + + " actingVersion uint64\n" + + "%7$s\n" + + "}\n\n" + + + " func (m *%1$s) Wrap(\n" + + " buffer []byte,\n" + + " offset uint64,\n" + + " actingVersion uint64,\n" + + " bufferLength uint64) {\n" + + " m.buffer = buffer\n" + + " m.bufferLength = bufferLength\n" + + " m.offset = offset\n" + + " m.actingVersion = actingVersion\n" + + " if !SbeNoBoundsCheck && (uint64(int(m.offset) + int(%2$s)) > m.bufferLength) {\n" + + " panic(\"buffer too short for flyweight [E107]\")\n" + + " }\n" + + " }\n\n" + + + " func (m *%1$s) EncodedLength() uint64 {\n" + + " return %2$s\n" + + " }\n\n" + + + " func (m *%1$s) Offset() uint64 {\n" + + " return m.offset\n" + + " }\n\n" + + + " func (m *%1$s) Buffer() []byte {\n" + + " return m.buffer\n" + + " }\n\n" + + + " func (m *%1$s) BufferLength() uint64 {\n" + + " return m.bufferLength\n" + + " }\n\n" + + + " func (m *%1$s) ActingVersion() uint64 {\n" + + " return m.actingVersion\n" + + " }\n\n" + + + " func (m *%1$s) SbeSchemaId() %3$s {\n" + + " return %4$s\n" + + " }\n\n" + + + " func (m *%1$s) SbeSchemaVersion() %5$s {\n" + + " return %6$s\n" + + " }\n", + + className, + sizeValue, + schemaIdType, + generateLiteral(ir.headerStructure().schemaIdType(), Integer.toString(ir.id())), + schemaVersionType, + generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version())), + sb); + } + + private CharSequence generateMessageFlyweightCode( + final String className, + final Token token, + final List fields, + final List groups) + { + final StringBuilder sb = new StringBuilder(); + final String blockLengthType = goTypeName(ir.headerStructure().blockLengthType()); + final String templateIdType = goTypeName(ir.headerStructure().templateIdType()); + final String schemaIdType = goTypeName(ir.headerStructure().schemaIdType()); + final String schemaVersionType = goTypeName(ir.headerStructure().schemaVersionType()); + final String semanticType = token.encoding().semanticType() == null ? "" : token.encoding().semanticType(); + final String headerType = ir.headerStructure().tokens().get(0).name(); + final String semanticVersion = ir.semanticVersion() == null ? "" : ir.semanticVersion(); + + sb.append(" buffer []byte\n" + + " bufferLength uint64\n" + + " offset uint64\n" + + " position uint64\n" + + " actingBlockLength uint64\n" + + " actingVersion uint64\n"); + + for (int i = 0, size = fields.size(); i < size; i++) + { + final Token signalToken = fields.get(i); + if (signalToken.signal() == Signal.BEGIN_FIELD) + { + final Token encodingToken = fields.get(i + 1); + final String propertyName = formatPropertyName(signalToken.name()); + final String typeName = formatClassName(encodingToken.applicableTypeName()); + + switch (encodingToken.signal()) + { + case BEGIN_SET: + new Formatter(sb).format(" _%1$s %2$s\n", + propertyName, + typeName); + break; + + case BEGIN_COMPOSITE: + new Formatter(sb).format(" _%1$s %2$s\n", + propertyName, + typeName); + break; + + default: + break; + } + } + } + + for (int i = 0, size = groups.size(); i < size; ) + { + final Token groupToken = groups.get(i); + if (groupToken.signal() == Signal.BEGIN_GROUP) + { + final String propertyName = formatPropertyName(groupToken.name()); + final String groupName = className + formatClassName(groupToken.name()); + + new Formatter(sb).format(" _%1$s %2$s\n", + propertyName, + groupName); + + i += groupToken.componentTokenCount(); + } + } + + sb.append("}\n\n"); + + sb.append(String.format( + " func (m *%10$s) sbePositionPtr() *uint64 {\n" + + " return &m.position\n" + + " }\n\n" + + + " const (\n" + + " %10$sSbeBlockLength %1$s = %2$s\n" + + " %10$sSbeTemplateID %3$s = %4$s\n" + + " %10$sSbeSchemaID %5$s = %6$s\n" + + " %10$sSbeSchemaVersion %7$s = %8$s\n" + + " %10$sSbeSemanticVersion string = \"%13$s\"\n" + + " )\n\n" + + + " func (m *%10$s) SbeBlockLength() %1$s {\n" + + " return %2$s\n" + + " }\n\n" + + + " func (m *%10$s) SbeBlockAndHeaderLength() uint64 {\n" + + " return m.EncodedLength() + uint64(m.SbeBlockLength())\n" + + " }\n\n" + + + " func (m *%10$s) SbeTemplateId() %3$s {\n" + + " return %4$s\n" + + " }\n\n" + + + " func (m *%10$s) SbeSchemaId() %5$s {\n" + + " return %6$s\n" + + " }\n\n" + + + " func (m *%10$s) SbeSchemaVersion() %7$s {\n" + + " return %8$s\n" + + " }\n\n" + + + " func (m *%10$s) SbeSemanticVersion() string {\n" + + " return \"%13$s\"\n" + + " }\n\n" + + + " func (m *%10$s) SbeSemanticType() string {\n" + + " return \"%9$s\"\n" + + " }\n\n" + + + " func (m *%10$s) Offset() uint64 {\n" + + " return m.offset\n" + + " }\n\n" + + + " func (m *%10$s) Wrap(\n" + + " buffer []byte,\n" + + " offset uint64,\n" + + " bufferLength uint64,\n" + + " actingBlockLength uint64,\n" + + " actingVersion uint64) *%10$s {\n" + + " m.buffer = buffer\n" + + " m.bufferLength = bufferLength\n" + + " m.offset = offset\n" + + " m.position = m.SbeCheckPosition(offset + actingBlockLength)\n" + + " m.actingBlockLength = actingBlockLength\n" + + " m.actingVersion = actingVersion\n" + + " return m\n" + + " }\n\n" + + + " func (m *%10$s) WrapForEncode(buffer []byte, offset uint64, bufferLength uint64) *%10$s {\n" + + " return m.Wrap(buffer, offset, bufferLength,\n" + + " uint64(m.SbeBlockLength()), uint64(m.SbeSchemaVersion()))\n" + + " }\n\n" + + + " func (m *%10$s) WrapAndApplyHeader(buffer []byte, offset uint64, bufferLength uint64) *%10$s {\n" + + " var hdr MessageHeader\n" + + " hdr.Wrap(buffer, offset, uint64(m.SbeSchemaVersion()), bufferLength)\n\n" + + + " hdr.SetBlockLength(m.SbeBlockLength()).\n" + + " SetTemplateId(m.SbeTemplateId()).\n" + + " SetSchemaId(m.SbeSchemaId()).\n" + + " SetVersion(m.SbeSchemaVersion())\n\n" + + + " return m.Wrap(\n" + + " buffer,\n" + + " offset + hdr.EncodedLength(),\n" + + " bufferLength,\n" + + " uint64(m.SbeBlockLength()),\n" + + " uint64(m.SbeSchemaVersion()))\n" + + " }\n\n" + + + " func (m *%10$s) WrapForDecode(\n" + + " buffer []byte,\n" + + " offset uint64,\n" + + " actingBlockLength uint64,\n" + + " actingVersion uint64," + + " bufferLength uint64) *%10$s {\n" + + " return m.Wrap(\n" + + " buffer,\n" + + " offset,\n" + + " bufferLength,\n" + + " actingBlockLength,\n" + + " actingVersion)\n" + + " }\n\n" + + + " func (m *%10$s) sbeRewind() *%10$s {\n" + + " return m.WrapForDecode(" + + "m.buffer, m.offset, m.actingBlockLength, m.actingVersion, m.bufferLength)\n" + + " }\n\n" + + + " func (m *%10$s) SbePosition() uint64 {\n" + + " return m.position\n" + + " }\n\n" + + + " func (m *%10$s) SbeCheckPosition(position uint64) uint64 {\n" + + " if !SbeNoBoundsCheck && (position > m.bufferLength) {\n" + + " panic(\"buffer too short [E100]\")\n" + + " }\n" + + " return position\n" + + " }\n\n" + + + " func (m *%10$s) SetSbePosition(position uint64) {\n" + + " m.position = m.SbeCheckPosition(position)\n" + + " }\n\n" + + + " func (m *%10$s) EncodedLength() uint64 {\n" + + " return m.SbePosition() - m.offset\n" + + " }\n\n" + + + // TODO generate length +// " func (m *%10$s) DecodeLength() uint64 {\n" + +// " var skipper %10$s\n" + +// " skipper.WrapForDecode(m.buffer, m.offset, uint64(m.SbeBlockLength()),\n" +// " uint64(m.ActingVersion()), m.bufferLength)\n" + +// " skipper.Skip()\n" + +// " return skipper.EncodedLength()\n" + +// " }\n\n" + + + " func (m *%10$s) Buffer() []byte {\n" + + " return m.buffer\n" + + " }\n\n" + + + " func (m *%10$s) BufferLength() uint64 {\n" + + " return m.bufferLength\n" + + " }\n\n" + + + " func (m *%10$s) ActingVersion() uint64 {\n" + + " return m.actingVersion\n" + + " }\n", + blockLengthType, + generateLiteral(ir.headerStructure().blockLengthType(), Integer.toString(token.encodedLength())), + templateIdType, + generateLiteral(ir.headerStructure().templateIdType(), Integer.toString(token.id())), + schemaIdType, + generateLiteral(ir.headerStructure().schemaIdType(), Integer.toString(ir.id())), + schemaVersionType, + generateLiteral(ir.headerStructure().schemaVersionType(), Integer.toString(ir.version())), + semanticType, + className, + generateConstructorsAndOperators(className), + formatClassName(headerType), + semanticVersion)); + return sb.toString(); + } + + private void generateFields( + final StringBuilder sb, + final String containingClassName, + final List tokens, + final String indent) + { + for (int i = 0, size = tokens.size(); i < size; i++) + { + final Token signalToken = tokens.get(i); + if (signalToken.signal() == Signal.BEGIN_FIELD) + { + final Token encodingToken = tokens.get(i + 1); + final String propertyName = formatPropertyName(signalToken.name()); + + generateFieldMetaAttributeMethod(sb, signalToken, indent, containingClassName); + generateFieldCommonMethods(indent, sb, signalToken, encodingToken, propertyName, containingClassName); + + switch (encodingToken.signal()) + { + case ENCODING: + generatePrimitiveProperty( + sb, containingClassName, propertyName, signalToken, encodingToken, indent); + break; + + case BEGIN_ENUM: + generateEnumProperty(sb, containingClassName, signalToken, propertyName, encodingToken, indent); + break; + + case BEGIN_SET: + generateBitsetProperty(sb, propertyName, encodingToken, indent, containingClassName); + break; + + case BEGIN_COMPOSITE: + generateCompositeProperty(sb, propertyName, encodingToken, indent, containingClassName); + break; + + default: + break; + } + } + } + } + + private void generateFieldCommonMethods( + final String indent, + final StringBuilder sb, + final Token fieldToken, + final Token encodingToken, + final String propertyName, + final String className) + { + new Formatter(sb).format("\n" + + indent + " func (m *%3$s) %1$sId() int {\n" + + indent + " return %2$d\n" + + indent + " }\n", + propertyName, + fieldToken.id(), + className); + + final int version = fieldToken.version(); + final String versionCheck = 0 == version ? + " return true\n" : " return m.actingVersion >= m.%1$sSinceVersion()\n"; + new Formatter(sb).format("\n" + + indent + " func (m *%3$s) %1$sSinceVersion() uint64 {\n" + + indent + " return %2$d\n" + + indent + " }\n\n" + + + indent + " func (m *%3$s) %1$sInActingVersion() bool {\n" + + indent + versionCheck + + indent + " }\n", + propertyName, + version, + className); + + new Formatter(sb).format("\n" + + indent + " func (m *%3$s) %1$sEncodingOffset() uint {\n" + + indent + " return %2$d\n" + + indent + " }\n", + propertyName, + encodingToken.offset(), + className); + } + + private void generateEnumProperty( + final StringBuilder sb, + final String containingClassName, + final Token fieldToken, + final String propertyName, + final Token encodingToken, + final String indent) + { + final String enumName = formatClassName(encodingToken.applicableTypeName()); + final PrimitiveType primitiveType = encodingToken.encoding().primitiveType(); + final String typeName = goTypeName(primitiveType); + final int offset = encodingToken.offset(); + + new Formatter(sb).format("\n" + + indent + " func (m *%1$s) %2$sEncodingLength() int {\n" + + indent + " return %3$d\n" + + indent + " }\n", + containingClassName, + propertyName, + fieldToken.encodedLength()); + + if (fieldToken.isConstantEncoding()) + { + final String constValue = fieldToken.encoding().constValue().toString(); + + new Formatter(sb).format("\n" + + indent + " func (m *%1$s) %2$sConstValue() %4$s {\n" + + indent + " return %4$s_%3$s\n" + + indent + " }\n", + containingClassName, + propertyName, + constValue.substring(constValue.indexOf(".") + 1), + enumName); + + new Formatter(sb).format("\n" + + indent + " func (m *%1$s) %2$s() %5$s {\n" + + "%3$s" + + indent + " return %5$s_%4$s\n" + + indent + " }\n", + containingClassName, + propertyName, + generateEnumFieldNotPresentCondition(fieldToken.version(), enumName, indent), + constValue.substring(constValue.indexOf(".") + 1), + enumName); + + new Formatter(sb).format("\n" + + indent + " func (m *%3$s) %2$sRaw() %1$s {\n" + + indent + " return %1$s(%5$s_%4$s)\n" + + indent + " }\n", + typeName, + propertyName, + containingClassName, + constValue.substring(constValue.indexOf(".") + 1), + enumName); + + } + else + { + final String offsetStr = Integer.toString(offset); + new Formatter(sb).format("\n" + + indent + " func (m *%5$s) %2$sRaw() %1$s {\n" + + "%3$s" + + "%4$s" + + indent + " }\n", + typeName, + propertyName, + generateFieldNotPresentCondition(fieldToken.version(), encodingToken.encoding(), indent), + generateLoadValue(primitiveType, offsetStr, encodingToken.encoding().byteOrder(), indent), + containingClassName); + + new Formatter(sb).format("\n" + + indent + " func (m *%1$s) %2$s() %7$s {\n" + + "%3$s" + + indent + " return %7$s(%4$s(m.buffer[(m.offset + %6$d):]))\n" + + indent + " }\n", + containingClassName, + propertyName, + generateEnumFieldNotPresentCondition(fieldToken.version(), enumName, indent), + formatReadBytes(encodingToken.encoding().byteOrder(), primitiveType), + typeName, + offset, + enumName); + + new Formatter(sb).format("\n" + + indent + " func (m *%1$s) Set%2$s(value %3$s) *%1$s {\n" + + indent + " val := %4$s(value)\n" + + indent + " %6$s(m.buffer[(m.offset + %5$d):], val)\n" + + indent + " return m\n" + + indent + " }\n", + formatClassName(containingClassName), + propertyName, + enumName, + typeName, + offset, + formatWriteBytes(encodingToken.encoding().byteOrder(), primitiveType)); + } + } + + private CharSequence generateNullValueLiteral( + final PrimitiveType primitiveType, + final Encoding encoding) + { + // Go does not handle minimum integer values properly + // So null values get special handling + if (null == encoding.nullValue()) + { + switch (primitiveType) + { + case CHAR: + return "SbeNullValueByte()"; + case FLOAT: + return "SbeNullValueFloat()"; + case DOUBLE: + return "SbeNullValueDouble()"; + case INT8: + return "SbeNullValueInt8()"; + case INT16: + return "SbeNullValueInt16()"; + case INT32: + return "SbeNullValueInt32()"; + case INT64: + return "SbeNullValueInt64()"; + case UINT8: + return "SbeNullValueUint8()"; + case UINT16: + return "SbeNullValueUint16()"; + case UINT32: + return "SbeNullValueUint32()"; + case UINT64: + return "SbeNullValueUint64()"; + } + } + + return generateLiteral(primitiveType, encoding.applicableNullValue().toString()); + } + + + private CharSequence generateLiteral(final PrimitiveType type, final String value) + { + String literal = ""; + + switch (type) + { + case CHAR: + literal = "byte(" + value + ")"; + break; + case UINT8: + literal = "uint8(" + value + ")"; + break; + case UINT16: + literal = "uint16(" + value + ")"; + break; + case INT8: + literal = "int8(" + value + ")"; + break; + case INT16: + literal = "int16(" + value + ")"; + break; + + case UINT32: + literal = "uint32(0x" + Integer.toHexString((int)Long.parseLong(value)) + ")"; + break; + + case INT32: + final long intValue = Long.parseLong(value); + if (intValue == Integer.MIN_VALUE) + { + literal = "math.MinInt32"; + addInclude("math"); + } + else + { + literal = "int32(" + value + ")"; + } + break; + + case FLOAT: + if (value.endsWith("NaN")) + { + literal = "math.NaN()"; + } + else + { + literal = value; + } + break; + + case INT64: + final long longValue = Long.parseLong(value); + if (longValue == Long.MIN_VALUE) + { + literal = "math.MinInt64"; + addInclude("math"); + } + else + { + literal = "int64(" + value + ")"; + } + break; + + case UINT64: + literal = "uint64(0x" + Long.toHexString(Long.parseLong(value)) + ")"; + break; + + case DOUBLE: + if (value.endsWith("NaN")) + { + literal = "math.NaN()"; + addInclude("math"); + } + else + { + literal = value; + } + break; + } + + return literal; + } + + private void addInclude(final String name) + { + includes.add(name); + } + + + private void generateDisplay( + final StringBuilder sb, + final String containingClassName, + final String name, + final List fields, + final List groups, + final List varData) + { + addInclude("strings"); + addInclude("fmt"); + new Formatter(sb).format("\n" + + "func (writer *%1$s) String() string {\n" + + " var builder strings.Builder\n" + + " builder.WriteString(\"{\\\"Name\\\": \\\"%1$s\\\", \")\n" + + " builder.WriteString(fmt.Sprintf(\"\\\"sbeTemplateId\\\": %%d\", writer.SbeTemplateId()))\n" + + " builder.WriteString(\", \")\n\n" + + "%2$s" + + " builder.WriteString(\"}\")\n\n" + + " return builder.String()\n" + + "}\n", + formatClassName(name), + appendDisplay(containingClassName, fields, groups, varData, INDENT)); + } + + + private CharSequence generateGroupDisplay( + final String groupClassName, + final List fields, + final List groups, + final List varData, + final String indent) + { + addInclude("strings"); + return String.format("\n" + + indent + "func (writer *%1$s) String() string {\n" + + indent + " builder := strings.Builder{}\n" + + indent + " builder.WriteString(\"{\")\n" + + "%2$s" + + indent + " builder.WriteString(\"}\")\n\n" + + indent + " return builder.String()\n" + + indent + "}\n", + groupClassName, + appendDisplay(groupClassName, fields, groups, varData, indent + INDENT)); + } + + + private CharSequence generateCompositeDisplay(final String name, final List tokens) + { + addInclude("strings"); + return String.format("\n" + + "func (writer *%1$s) String() string {\n" + + " builder := strings.Builder{}\n" + + " builder.WriteString(\"{\")\n" + + "%2$s" + + " builder.WriteString(\"}\")\n\n" + + " return builder.String()\n" + + "}\n\n", + formatClassName(name), + appendDisplay(formatClassName(name), tokens, new ArrayList<>(), new ArrayList<>(), INDENT)); + } + + + private CharSequence appendDisplay( + final String containingClassName, + final List fields, + final List groups, + final List varData, + final String indent) + { + final StringBuilder sb = new StringBuilder(); + final boolean[] atLeastOne = {false}; + + for (int i = 0, size = fields.size(); i < size; ) + { + final Token fieldToken = fields.get(i); + final Token encodingToken = fields.get(fieldToken.signal() == Signal.BEGIN_FIELD ? i + 1 : i); + + writeTokenDisplay(sb, fieldToken.name(), encodingToken, atLeastOne, indent); + i += fieldToken.componentTokenCount(); + } + + for (int i = 0, size = groups.size(); i < size; i++) + { + final Token groupToken = groups.get(i); + if (groupToken.signal() != Signal.BEGIN_GROUP) + { + throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken); + } + + if (atLeastOne[0]) + { + sb.append(indent).append("builder.WriteString(\", \")\n"); + } + atLeastOne[0] = true; + + new Formatter(sb).format( + indent + "{\n" + + indent + " atLeastOne := false\n" + + indent + " builder.WriteString(`\"%3$s\": [`)\n" + + indent + " writer.%2$s().ForEach(\n" + + indent + " func(value *%1$s) {\n" + + indent + " if atLeastOne {\n" + + indent + " builder.WriteString(\", \")\n" + + indent + " }\n" + + indent + " atLeastOne = true\n" + + indent + " builder.WriteString(value.String())\n" + + indent + " })\n" + + indent + " builder.WriteString(\"]\")\n" + + indent + "}\n\n", + containingClassName + formatClassName(groupToken.name()), + formatPropertyName(groupToken.name()), + groupToken.name()); + + i = findEndSignal(groups, i, Signal.END_GROUP, groupToken.name()); + } + + for (int i = 0, size = varData.size(); i < size; ) + { + final Token varDataToken = varData.get(i); + if (varDataToken.signal() != Signal.BEGIN_VAR_DATA) + { + throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + varDataToken); + } + + if (atLeastOne[0]) + { + sb.append(indent).append("builder.WriteString(\", \")\n"); + } + atLeastOne[0] = true; + + final String characterEncoding = varData.get(i + 3).encoding().characterEncoding(); + sb.append(indent).append("builder.WriteString(`\"").append(varDataToken.name()).append("\": `)\n"); + + if (null == characterEncoding) + { + final String skipFunction = "writer.Skip" + toUpperFirstChar(varDataToken.name()) + "()"; + + sb.append(indent).append("builder.WriteString(`\"`)\n") + .append(indent).append(INDENT).append(skipFunction).append("\n") + .append(INDENT).append("builder.WriteString(\" bytes of raw data\")\n"); + } + else + { + final String getAsStringFunction = + "writer.Get" + toUpperFirstChar(varDataToken.name()) + "AsJsonEscapedString()"; + + sb.append(indent).append("builder.WriteString(`\"` + ") + .append(getAsStringFunction).append(" + `\"`);\n\n"); + } + + i += varDataToken.componentTokenCount(); + } + + return sb; + } + + private void writeTokenDisplay( + final StringBuilder sb, + final String fieldTokenName, + final Token typeToken, + final boolean[] atLeastOne, + final String indent) + { + if (typeToken.encodedLength() <= 0 || typeToken.isConstantEncoding()) + { + return; + } + + if (atLeastOne[0]) + { + sb.append(indent).append("builder.WriteString(\", \")\n"); + } + else + { + atLeastOne[0] = true; + } + + sb.append(indent).append("builder.WriteString(`\"").append(fieldTokenName).append("\": `)\n"); + final String fieldName = "writer." + formatPropertyName(fieldTokenName); + + switch (typeToken.signal()) + { + case ENCODING: + if (typeToken.arrayLength() > 1) + { + if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR) + { + final String getAsStringFunction = + "writer.Get" + toUpperFirstChar(fieldTokenName) + "AsJsonEscapedString()"; + + sb.append(indent).append("builder.WriteString(`\"` +\n").append(indent).append(INDENT) + .append(getAsStringFunction).append(" + `\"`)\n"); + } + else + { + addInclude("fmt"); + sb.append( + indent + "builder.WriteString(\"[\")\n" + + indent + "for i := 0; i < " + fieldName + "Length(); i++ {\n" + + indent + " if i > 0 {\n" + + indent + " builder.WriteString(\",\")\n" + + indent + " }\n" + + indent + " builder.WriteString(fmt.Sprintf(\"%v\", " + fieldName + + "Index(uint64(i))))\n" + + indent + "}\n" + + indent + "builder.WriteString(\"]\")\n"); + } + } + else + { + addInclude("fmt"); + + if (typeToken.encoding().primitiveType() == PrimitiveType.CHAR) + { + sb.append(indent).append("builder.WriteString(fmt.Sprintf(`\"%c\"`, ").append(fieldName) + .append("()))\n"); + } + else + { + sb.append(indent).append("builder.WriteString(fmt.Sprintf(`\"%v\"`, ").append(fieldName) + .append("()))\n"); + } + } + break; + + case BEGIN_ENUM: + addInclude("fmt"); + sb.append(indent).append("builder.WriteString(fmt.Sprintf(`\"%v\"`, ").append(fieldName) + .append("()))\n"); + break; + + case BEGIN_SET: + case BEGIN_COMPOSITE: + if (0 == typeToken.version()) + { + sb.append(indent).append("builder.WriteString(").append(fieldName).append("().String())\n"); + } + else + { + new Formatter(sb).format( + indent + "if (%1$sInActingVersion()) {\n" + + indent + " builder.WriteString(%1$s().String())\n" + + indent + "} else {\n" + + indent + " builder.WriteString(%2$s)\n" + + indent + "}\n", + fieldName, + typeToken.signal() == Signal.BEGIN_SET ? "\"[]\"" : "\"{}\""); + } + break; + + default: + break; + } + + sb.append('\n'); + } + + + private CharSequence generateChoicesDisplay( + final String name, + final List tokens) + { + final String indent = INDENT; + final StringBuilder sb = new StringBuilder(); + final List choiceTokens = new ArrayList<>(); + + collect(Signal.CHOICE, tokens, 0, choiceTokens); + + addInclude("strings"); + new Formatter(sb).format("\n" + + indent + "func (writer %1$s) String() string {\n" + + indent + " builder := strings.Builder{}\n" + + indent + " builder.WriteString(\"[\")\n", + name); + + if (choiceTokens.size() > 1) + { + sb.append(indent + " atLeastOne := false\n"); + } + + for (int i = 0, size = choiceTokens.size(); i < size; i++) + { + final Token token = choiceTokens.get(i); + final String choiceName = "writer." + formatPropertyName(token.name()); + + sb.append(indent + " if ").append(choiceName).append("() {\n"); + + if (i > 0) + { + sb.append( + indent + " if (atLeastOne) {\n" + + indent + " builder.WriteString(\",\")\n" + + indent + " }\n"); + } + sb.append(indent + " builder.WriteString(`\"").append(formatPropertyName(token.name())) + .append("\"`)\n"); + + if (i < (size - 1)) + { + sb.append(indent + " atLeastOne = true\n"); + } + + sb.append(indent + " }\n"); + } + + sb.append( + indent + " builder.WriteString(\"]\")\n" + + indent + " return builder.String()\n" + + indent + "}\n"); + + return sb; + } + + + private CharSequence generateEnumDisplay( + final List tokens, + final Token encodingToken) + { + final String enumName = formatClassName(encodingToken.applicableTypeName()); + final StringBuilder sb = new StringBuilder(); + + new Formatter(sb).format("\n" + + " func (value %1$s) String() string {\n" + + " switch value {\n", + enumName); + + for (final Token token : tokens) + { + new Formatter(sb).format( + " case %2$s_%1$s: return \"%1$s\"\n", + token.name(), + enumName); + } + + sb.append(" case ").append(enumName) + .append("_NULL_VALUE: return \"NULL_VALUE\"\n").append(" }\n\n"); + + if (shouldDecodeUnknownEnumValues) + { + sb.append(" return \"SBE_UNKNOWN\"\n").append(" }\n\n"); + } + else + { + addInclude("fmt"); + new Formatter(sb).format( + " panic(fmt.Sprintf(\"unknown value for enum %1$s [E103]: %%d\", value))\n" + + " }\n\n", + enumName); + } + + return sb; + } + + + private Object[] generateMessageLengthArgs( + final List groups, + final List varData, + final String indent, + final boolean withName) + { + final StringBuilder sb = new StringBuilder(); + int count = 0; + + for (int i = 0, size = groups.size(); i < size; i++) + { + final Token groupToken = groups.get(i); + if (groupToken.signal() != Signal.BEGIN_GROUP) + { + throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken); + } + + final int endSignal = findEndSignal(groups, i, Signal.END_GROUP, groupToken.name()); + final String groupName = formatPropertyName(groupToken.name()); + + if (count > 0) + { + sb.append(",\n").append(indent); + } + + final List thisGroup = groups.subList(i, endSignal + 1); + + if (isMessageConstLength(thisGroup)) + { + if (withName) + { + sb.append(" ").append(groupName).append("Length "); + } + sb.append("int"); + } + else + { + if (withName) + { + sb.append(groupName).append("ItemLengths "); + } + + sb.append("[]struct{"); + sb.append(generateMessageLengthArgs(thisGroup, indent + INDENT, false)[0]); + sb.append("}"); + } + + count += 1; + + i = endSignal; + } + + for (int i = 0, size = varData.size(); i < size; ) + { + final Token varDataToken = varData.get(i); + if (varDataToken.signal() != Signal.BEGIN_VAR_DATA) + { + throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + varDataToken); + } + + if (count > 0) + { + sb.append(",\n").append(indent); + } + + if (withName) + { + sb.append(" ").append(formatPropertyName(varDataToken.name())).append("Length").append(" int"); + } + + count += 1; + + i += varDataToken.componentTokenCount(); + } + + CharSequence result = sb; + if (count > 1) + { + result = "\n" + indent + result; + } + + return new Object[] {result, count}; + } + + + private Object[] generateMessageLengthArgs(final List tokens, final String indent, final boolean withName) + { + int i = 0; + + final Token groupToken = tokens.get(i); + if (groupToken.signal() != Signal.BEGIN_GROUP) + { + throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken); + } + + ++i; + final int groupHeaderTokenCount = tokens.get(i).componentTokenCount(); + i += groupHeaderTokenCount; + + final List fields = new ArrayList<>(); + i = collectFields(tokens, i, fields); + + final List groups = new ArrayList<>(); + i = collectGroups(tokens, i, groups); + + final List varData = new ArrayList<>(); + collectVarData(tokens, i, varData); + + return generateMessageLengthArgs(groups, varData, indent, withName); + } + + private boolean isMessageConstLength(final List tokens) + { + final Integer count = (Integer)generateMessageLengthArgs(tokens, BASE_INDENT, false)[1]; + + return count == 0; + } + + private CharSequence generateMessageLength( + final List groups, + final List varData, + final String indent, + final String className) + { + final StringBuilder sbEncode = new StringBuilder(); + final StringBuilder sbSkip = new StringBuilder(); + + for (int i = 0, size = groups.size(); i < size; i++) + { + final Token groupToken = groups.get(i); + + if (groupToken.signal() != Signal.BEGIN_GROUP) + { + throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken); + } + + final int endSignal = findEndSignal(groups, i, Signal.END_GROUP, groupToken.name()); + final List thisGroup = groups.subList(i, endSignal + 1); + + final Token numInGroupToken = Generators.findFirst("numInGroup", groups, i); + final long minCount = numInGroupToken.encoding().applicableMinValue().longValue(); + final long maxCount = numInGroupToken.encoding().applicableMaxValue().longValue(); + + final String countName = formatPropertyName(groupToken.name()) + + (isMessageConstLength(thisGroup) ? "Length" : "len(ItemLengths)"); + + final String minCheck = minCount > 0 ? countName + " < " + minCount + " || " : ""; + final String maxCheck = countName + " > " + maxCount; + + new Formatter(sbEncode).format("\n" + + indent + " length += m.%1$s().SbeHeaderSize()\n", + formatPropertyName(groupToken.name())); + + if (isMessageConstLength(thisGroup)) + { + addInclude("fmt"); + new Formatter(sbEncode).format( + indent + " if (%3$s%4$s) {\n" + + indent + " panic(fmt.Sprintf(\"%5$s outside of allowed range [E110]\"))\n" + + indent + " }\n" + + indent + " length += uint64(%1$sLength) * m.%1$s().SbeBlockLength()\n", + formatPropertyName(groupToken.name()), + formatClassName(groupToken.name()), + minCheck, + maxCheck, + countName); + } + else + { + new Formatter(sbEncode).format( + indent + " if (%3$s%4$s) {\n" + + indent + " panic(\"%5$s outside of allowed range [E110]\")\n" + + indent + " }\n\n" + + indent + " for _, e := range %1$sItemLengths {\n" + + indent + " length += %2$s.ComputeLength(0)\n" + + indent + " }\n", + formatPropertyName(groupToken.name()), + formatClassName(groupToken.name()), + minCheck, + maxCheck, + countName); + } + + new Formatter(sbSkip).format( + indent + (" for %1$sGroup := m.%1$s(); %1$sGroup.HasNext(); {\n") + + indent + (" %1$sGroup.Next().Skip()\n") + + indent + (" }\n"), + formatPropertyName(groupToken.name())); + + i = endSignal; + } + for (int i = 0, size = varData.size(); i < size; ) + { + final Token varDataToken = varData.get(i); + + if (varDataToken.signal() != Signal.BEGIN_VAR_DATA) + { + throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + varDataToken); + } + + final Token lengthToken = Generators.findFirst("length", varData, i); + + addInclude("fmt"); + new Formatter(sbEncode).format("\n" + + indent + " length += m.%1$sHeaderLength()\n" + + indent + " if m.%1$sLength() > %2$d {\n" + + indent + " return 0, fmt.Errorf(\"%1$sLength too long for length type [E109]\")\n" + + indent + " }\n" + + indent + " length += uint64(m.%1$sLength())\n", + formatPropertyName(varDataToken.name()), + lengthToken.encoding().applicableMaxValue().longValue()); + + new Formatter(sbSkip).format( + indent + " m.Skip%1$s()\n", + toUpperFirstChar(varDataToken.name())); + + i += varDataToken.componentTokenCount(); + } + + final StringBuilder sb = new StringBuilder(); + + new Formatter(sb).format("\n" + + indent + "func (m *%2$s) Skip() {\n" + + sbSkip + + indent + "}\n\n" + + + indent + "func (m *%2$s) IsConstLength() bool {\n" + + indent + " return " + (groups.isEmpty() && varData.isEmpty()) + "\n" + + indent + "}\n\n" + + + indent + "func (m *%2$s) ComputeLength(%1$s) (int, error) {\n" + + indent + " length := m.SbeBlockLength()\n" + + sbEncode + "\n" + + indent + " return int(length), nil\n" + + indent + "}\n", + generateMessageLengthArgs(groups, varData, indent + INDENT, true)[0], + className); + + return sb; + } +} diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightOutputManager.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightOutputManager.java new file mode 100644 index 0000000000..954803ca29 --- /dev/null +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightOutputManager.java @@ -0,0 +1,72 @@ +/* + * Copyright 2013-2023 Real Logic Limited. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package uk.co.real_logic.sbe.generation.golang.flyweight; + +import org.agrona.generation.OutputManager; +import org.agrona.Verify; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +import static java.io.File.separatorChar; + +/** + * {@link OutputManager} for managing the creation of golang source files as the target of code generation. + * The character encoding for the {@link java.io.Writer} is UTF-8. + */ +public class GolangFlyweightOutputManager implements OutputManager +{ + private final File outputDir; + + /** + * Create a new {@link OutputManager} for generating golang source files into a given package. + * + * @param baseDirName for the generated source code. + * @param namespaceName for the generated source code relative to the baseDirName. + */ + public GolangFlyweightOutputManager(final String baseDirName, final String namespaceName) + { + Verify.notNull(baseDirName, "baseDirName"); + Verify.notNull(namespaceName, "applicableNamespace"); + + final String dirName = baseDirName.endsWith("" + separatorChar) ? baseDirName : baseDirName + separatorChar; + final String packageDirName = dirName + namespaceName.replace('.', '_'); + + outputDir = new File(packageDirName); + if (!outputDir.exists() && !outputDir.mkdirs()) + { + throw new IllegalStateException("Unable to create directory: " + packageDirName); + } + } + + /** + * Create a new output which will be a golang source file in the given package. + *

+ * The {@link java.io.Writer} should be closed once the caller has finished with it. The Writer is + * buffered for efficient IO operations. + * + * @param name the name of the golang class. + * @return a {@link java.io.Writer} to which the source code should be written. + * @throws IOException if an issue occurs when creating the file. + */ + public Writer createOutput(final String name) throws IOException + { + final File targetFile = new File(outputDir, name + ".go"); + + return Files.newBufferedWriter(targetFile.toPath(), StandardCharsets.UTF_8); + } +} diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightUtil.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightUtil.java new file mode 100644 index 0000000000..21509c1472 --- /dev/null +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightUtil.java @@ -0,0 +1,224 @@ +/* + * Copyright 2013-2023 Real Logic Limited. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package uk.co.real_logic.sbe.generation.golang.flyweight; + +import uk.co.real_logic.sbe.PrimitiveType; +import uk.co.real_logic.sbe.SbeTool; +import uk.co.real_logic.sbe.ValidationUtil; + +import java.nio.ByteOrder; +import java.util.EnumMap; +import java.util.Map; + +import static uk.co.real_logic.sbe.generation.Generators.toUpperFirstChar; + +/** + * Utilities for mapping between IR and the Golang language. + */ +public class GolangFlyweightUtil +{ + private static final Map PRIMITIVE_TYPE_STRING_ENUM_MAP = new EnumMap<>(PrimitiveType.class); + private static final Map MARSHAL_TYPE_BY_PRIMITIVE_TYPE_MAP = + new EnumMap<>(PrimitiveType.class); + + static + { + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.CHAR, "byte"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.INT8, "int8"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.INT16, "int16"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.INT32, "int32"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.INT64, "int64"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.UINT8, "uint8"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.UINT16, "uint16"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.UINT32, "uint32"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.UINT64, "uint64"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.FLOAT, "float32"); + PRIMITIVE_TYPE_STRING_ENUM_MAP.put(PrimitiveType.DOUBLE, "float64"); + } + + static + { + MARSHAL_TYPE_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.CHAR, "Bytes"); + MARSHAL_TYPE_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.INT8, "Int8"); + MARSHAL_TYPE_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.INT16, "Int16"); + MARSHAL_TYPE_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.INT32, "Int32"); + MARSHAL_TYPE_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.INT64, "Int64"); + MARSHAL_TYPE_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.UINT8, "Uint8"); + MARSHAL_TYPE_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.UINT16, "Uint16"); + MARSHAL_TYPE_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.UINT32, "Uint32"); + MARSHAL_TYPE_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.UINT64, "Uint64"); + MARSHAL_TYPE_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.FLOAT, "Float32"); + MARSHAL_TYPE_BY_PRIMITIVE_TYPE_MAP.put(PrimitiveType.DOUBLE, "Float64"); + } + + /** + * Map the name of a {@link uk.co.real_logic.sbe.PrimitiveType} to a Golang primitive type name. + * + * @param primitiveType to map. + * @return the name of the Java primitive that most closely maps. + */ + public static String goTypeName(final PrimitiveType primitiveType) + { + return PRIMITIVE_TYPE_STRING_ENUM_MAP.get(primitiveType); + } + + /** + * Format a String as a class name. + * + * @param value to be formatted. + * @return the string formatted as a class name. + */ + public static String formatClassName(final String value) + { + return toUpperFirstChar(value); + } + + /** + * Map the name of a {@link uk.co.real_logic.sbe.PrimitiveType} to a Golang marhsalling function name. + * + * @param primitiveType to map. + * @return the name of the Java primitive that most closely maps. + */ + public static String golangMarshalType(final PrimitiveType primitiveType) + { + return MARSHAL_TYPE_BY_PRIMITIVE_TYPE_MAP.get(primitiveType); + } + + /** + * Format a String as a property name. + * + * @param value to be formatted. + * @return the string formatted as a property name. + */ + public static String formatPropertyName(final String value) + { + String formattedValue = toUpperFirstChar(value); + + if (ValidationUtil.isGolangKeyword(formattedValue)) + { + final String keywordAppendToken = System.getProperty(SbeTool.KEYWORD_APPEND_TOKEN); + if (null == keywordAppendToken) + { + throw new IllegalStateException( + "Invalid property name='" + formattedValue + + "' please correct the schema or consider setting system property: " + + SbeTool.KEYWORD_APPEND_TOKEN); + } + + formattedValue += keywordAppendToken; + } + + return formattedValue; + } + + /** + * Generate a count of closing braces, one on each line. + * + * @param count of closing braces. + * @return A string with count of closing braces. + */ + public static String closingBraces(final int count) + { + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < count; i++) + { + sb.append("}\n"); + } + + return sb.toString(); + } + + /** + * Return the Golang formatted byte order encoding string to use to read for a given byte order and primitiveType + * + * @param byteOrder of the {@link uk.co.real_logic.sbe.ir.Token} + * @param primitiveType of the {@link uk.co.real_logic.sbe.ir.Token} + * @return the string formatted as the byte ordering encoding + */ + public static String formatReadBytes(final ByteOrder byteOrder, final PrimitiveType primitiveType) + { + final String suffix = (byteOrder == ByteOrder.BIG_ENDIAN ? + "BigEndian" : "LittleEndian"); + + switch (primitiveType) + { + case CHAR: + return "Byte" + suffix; + case FLOAT: + return "Float" + suffix; + case DOUBLE: + return "Double" + suffix; + case INT8: + return "Int8" + suffix; + case INT16: + return "Int16" + suffix; + case INT32: + return "Int32" + suffix; + case INT64: + return "Int64" + suffix; + case UINT8: + return "Uint8" + suffix; + case UINT16: + return "Uint16" + suffix; + case UINT32: + return "Uint32" + suffix; + case UINT64: + return "Uint64" + suffix; + } + return ""; + } + + /** + * Return the Golang formatted byte order encoding string to use to write for a given byte order and primitiveType + * + * @param byteOrder of the {@link uk.co.real_logic.sbe.ir.Token} + * @param primitiveType of the {@link uk.co.real_logic.sbe.ir.Token} + * @return the string formatted as the byte ordering encoding + */ + public static String formatWriteBytes(final ByteOrder byteOrder, final PrimitiveType primitiveType) + { + final String suffix = (byteOrder == ByteOrder.BIG_ENDIAN ? + "BigEndian" : "LittleEndian"); + + switch (primitiveType) + { + case CHAR: + return "PutByte" + suffix; + case FLOAT: + return "PutFloat" + suffix; + case DOUBLE: + return "PutDouble" + suffix; + case INT8: + return "PutInt8" + suffix; + case INT16: + return "PutInt16" + suffix; + case INT32: + return "PutInt32" + suffix; + case INT64: + return "PutInt64" + suffix; + case UINT8: + return "PutUint8" + suffix; + case UINT16: + return "PutUint16" + suffix; + case UINT32: + return "PutUint32" + suffix; + case UINT64: + return "PutUint64" + suffix; + } + return ""; + } + +} diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/GolangGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/struct/GolangGenerator.java similarity index 99% rename from sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/GolangGenerator.java rename to sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/struct/GolangGenerator.java index 1c1582380a..58c357951d 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/GolangGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/struct/GolangGenerator.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package uk.co.real_logic.sbe.generation.golang; +package uk.co.real_logic.sbe.generation.golang.struct; import uk.co.real_logic.sbe.PrimitiveType; import uk.co.real_logic.sbe.generation.CodeGenerator; @@ -37,7 +37,7 @@ import static uk.co.real_logic.sbe.PrimitiveType.CHAR; import static uk.co.real_logic.sbe.generation.Generators.toUpperFirstChar; -import static uk.co.real_logic.sbe.generation.golang.GolangUtil.*; +import static uk.co.real_logic.sbe.generation.golang.struct.GolangUtil.*; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectVarData; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectGroups; import static uk.co.real_logic.sbe.ir.GenerationUtil.collectFields; diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/GolangOutputManager.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/struct/GolangOutputManager.java similarity index 98% rename from sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/GolangOutputManager.java rename to sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/struct/GolangOutputManager.java index 1675d69fed..a584396a09 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/GolangOutputManager.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/struct/GolangOutputManager.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package uk.co.real_logic.sbe.generation.golang; +package uk.co.real_logic.sbe.generation.golang.struct; import org.agrona.generation.OutputManager; import org.agrona.Verify; diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/GolangUtil.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/struct/GolangUtil.java similarity index 98% rename from sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/GolangUtil.java rename to sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/struct/GolangUtil.java index 0aa379216b..2689da26de 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/GolangUtil.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/struct/GolangUtil.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package uk.co.real_logic.sbe.generation.golang; +package uk.co.real_logic.sbe.generation.golang.struct; import uk.co.real_logic.sbe.PrimitiveType; import uk.co.real_logic.sbe.SbeTool; From ed9de073afd92318551d9e2ba3b35eefad152d41 Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Tue, 8 Aug 2023 09:15:01 -0400 Subject: [PATCH 02/21] [Go] Checkstyle fix --- .../golang/flyweight/GolangFlyweightGenerator.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java index 55b0265c94..2579d913c5 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java @@ -57,7 +57,10 @@ public class GolangFlyweightGenerator implements CodeGenerator * @param shouldDecodeUnknownEnumValues generate support for unknown enum values when decoding. * @param outputManager for generating the codecs to. */ - public GolangFlyweightGenerator(final Ir ir, final boolean shouldDecodeUnknownEnumValues, final OutputManager outputManager) + public GolangFlyweightGenerator( + final Ir ir, + final boolean shouldDecodeUnknownEnumValues, + final OutputManager outputManager) { Verify.notNull(ir, "ir"); Verify.notNull(outputManager, "outputManager"); @@ -1791,7 +1794,6 @@ private void generateArrayProperty( encodingToken.encoding().byteOrder(), indent); - new Formatter(sb).format("\n" + indent + " func (m *%6$s) %2$sIndex(index uint64) %1$s {\n" + indent + " if (index >= %3$d) {\n" + From 5ccb4f12a4bd649fe05cc1e9338871ce1412812b Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Wed, 9 Aug 2023 21:12:40 -0400 Subject: [PATCH 03/21] [Go] Fix null string return value --- .../generation/golang/flyweight/GolangFlyweightGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java index 2579d913c5..4fc2d880c2 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java @@ -417,7 +417,7 @@ private static CharSequence generateTypeFieldNotPresentCondition( return String.format( indent + " if m.actingVersion < %1$d {\n" + - indent + " return nil\n" + + indent + " return \"\"\n" + indent + " }\n\n", sinceVersion); } From 41fbccd4cfa3c699837dd6b76e99766cce358a35 Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Wed, 9 Aug 2023 21:12:45 -0400 Subject: [PATCH 04/21] [Go] Remove spurious type definition for choices --- .../generation/golang/flyweight/GolangFlyweightGenerator.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java index 4fc2d880c2..9e3a1bec58 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java @@ -1385,10 +1385,6 @@ private CharSequence generateChoices(final String bitsetClassName, final List Date: Wed, 9 Aug 2023 21:47:30 -0400 Subject: [PATCH 05/21] [Go] Fix formatting in WrapForDecode --- .../generation/golang/flyweight/GolangFlyweightGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java index 9e3a1bec58..916885e4e5 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java @@ -2308,7 +2308,7 @@ private CharSequence generateMessageFlyweightCode( " buffer []byte,\n" + " offset uint64,\n" + " actingBlockLength uint64,\n" + - " actingVersion uint64," + + " actingVersion uint64,\n" + " bufferLength uint64) *%10$s {\n" + " return m.Wrap(\n" + " buffer,\n" + From 9db9df7b0a736991b901d8ff5fcbf14e5c274a07 Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Wed, 9 Aug 2023 23:28:41 -0400 Subject: [PATCH 06/21] [Go] Add returning an array by []byte --- .../flyweight/GolangFlyweightGenerator.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java index 916885e4e5..78643d2c15 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java @@ -1772,17 +1772,17 @@ private void generateArrayProperty( arrayLength, formatClassName(containingClassName)); - // TODO return slice from array -// new Formatter(sb).format("\n" + -// indent + " func (m *%5$s) %1$s() []%2$s {\n" + -// "%3$s" + -// indent + " return m.buffer[(m.offset + %4$d):]\n" + -// indent + " }\n", -// propertyName, -// goTypeName, -// generateTypeFieldNotPresentCondition(propertyToken.version(), indent), -// offset, -// formatClassName(containingClassName)); + new Formatter(sb).format("\n" + + indent + "func (m *%6$s) %2$s() []byte {\n" + + "%4$s" + + indent + " return m.buffer[m.offset+%5$d:m.offset+%5$d+%3$d]\n" + + indent + "}\n", + goTypeName, + toUpperFirstChar(propertyName), + arrayLength, + generateArrayFieldNotPresentCondition(propertyToken.version(), indent), + offset, + formatClassName(containingClassName)); final CharSequence loadValue = generateLoadValue( primitiveType, From dcfc861b6b16d6cbb934806b5e0855a7aac078f8 Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Wed, 9 Aug 2023 23:28:50 -0400 Subject: [PATCH 07/21] [Go] Fix array not present in version --- .../flyweight/GolangFlyweightGenerator.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java index 78643d2c15..2786857496 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java @@ -355,6 +355,21 @@ private static CharSequence generateArrayFieldNotPresentCondition( return ""; } + return String.format( + indent + " if m.actingVersion < %1$d {\n" + + indent + " return nil\n" + + indent + " }\n\n", + sinceVersion); + } + + private static CharSequence generateArrayLengthNotPresentCondition( + final int sinceVersion, final String indent) + { + if (0 == sinceVersion) + { + return ""; + } + return String.format( indent + " if m.actingVersion < %1$d {\n" + indent + " return 0\n" + @@ -1113,7 +1128,7 @@ private void generateVarData( indent + " return dataLength\n" + indent + " }\n", propertyName, - generateArrayFieldNotPresentCondition(token.version(), indent), + generateArrayLengthNotPresentCondition(token.version(), indent), lengthOfLengthField, lengthByteOrderReadStr, className); @@ -1153,7 +1168,7 @@ private void generateVarData( indent + " return int(bytesToCopy)\n" + indent + "}\n", propertyName, - generateArrayFieldNotPresentCondition(token.version(), indent), + generateArrayLengthNotPresentCondition(token.version(), indent), lengthOfLengthField, lengthByteOrderReadStr, className); @@ -1271,7 +1286,7 @@ private void generateVarDataDescriptors( indent + " return %3$s(m.buffer[m.SbePosition():])\n" + indent + "}\n", formatPropertyName(propertyName), - generateArrayFieldNotPresentCondition(version, BASE_INDENT), + generateArrayLengthNotPresentCondition(version, BASE_INDENT), formatReadBytes(lengthToken.encoding().byteOrder(), lengthToken.encoding().primitiveType()), lengthGoType, className); From 02d7f82674aff38a6ecb3d611fb8da29983f74e1 Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Wed, 9 Aug 2023 23:29:12 -0400 Subject: [PATCH 08/21] [Go] Add ComputeLength methods --- .../flyweight/GolangFlyweightGenerator.java | 172 ++++++++++++++---- 1 file changed, 132 insertions(+), 40 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java index 2786857496..b674528de7 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java @@ -680,8 +680,7 @@ public void generate() throws IOException generateGroups(sb, groups, BASE_INDENT, className, className); generateVarData(sb, className, varData, BASE_INDENT); generateDisplay(sb, msgToken.name(), className, fields, groups, varData); - // TODO generate length - // sb.append(generateMessageLength(groups, varData, BASE_INDENT, className)); + sb.append(generateMessageLength(groups, varData, BASE_INDENT, className)); out.append(sb); fileOut.append(generateFileHeader(ir.namespaces())); @@ -1080,8 +1079,7 @@ private void generateGroups( generateVarData(sb, groupClassName, varData, indent + INDENT); sb.append(generateGroupDisplay(groupClassName, fields, groups, varData, indent + INDENT + INDENT)); - // TODO generate length - // sb.append(generateMessageLength(groups, varData, indent + INDENT + INDENT, groupClassName)); + sb.append(generateMessageLength(groups, varData, indent + INDENT + INDENT, groupClassName)); generateGroupProperty(sb, groupName, groupClassName, outerClassName, groupToken, goTypeForNumInGroup, indent); @@ -2357,14 +2355,13 @@ private CharSequence generateMessageFlyweightCode( " return m.SbePosition() - m.offset\n" + " }\n\n" + - // TODO generate length -// " func (m *%10$s) DecodeLength() uint64 {\n" + -// " var skipper %10$s\n" + -// " skipper.WrapForDecode(m.buffer, m.offset, uint64(m.SbeBlockLength()),\n" -// " uint64(m.ActingVersion()), m.bufferLength)\n" + -// " skipper.Skip()\n" + -// " return skipper.EncodedLength()\n" + -// " }\n\n" + + " func (m *%10$s) DecodeLength() uint64 {\n" + + " var skipper %10$s\n" + + " skipper.WrapForDecode(m.buffer, m.offset, uint64(m.SbeBlockLength()),\n" + + " uint64(m.ActingVersion()), m.bufferLength)\n" + + " skipper.Skip()\n" + + " return skipper.EncodedLength()\n" + + " }\n\n" + " func (m *%10$s) Buffer() []byte {\n" + " return m.buffer\n" + @@ -3055,12 +3052,11 @@ private CharSequence generateEnumDisplay( return sb; } - private Object[] generateMessageLengthArgs( final List groups, final List varData, final String indent, - final boolean withName) + final boolean inStruct) { final StringBuilder sb = new StringBuilder(); int count = 0; @@ -3078,28 +3074,26 @@ private Object[] generateMessageLengthArgs( if (count > 0) { - sb.append(",\n").append(indent); + if (inStruct) + { + sb.append("; ").append(indent); + } + else + { + sb.append(",\n").append(indent); + } } final List thisGroup = groups.subList(i, endSignal + 1); if (isMessageConstLength(thisGroup)) { - if (withName) - { - sb.append(" ").append(groupName).append("Length "); - } - sb.append("int"); + sb.append(" ").append(groupName).append("Length ").append("int"); } else { - if (withName) - { - sb.append(groupName).append("ItemLengths "); - } - - sb.append("[]struct{"); - sb.append(generateMessageLengthArgs(thisGroup, indent + INDENT, false)[0]); + sb.append(groupName).append("ItemLengths ").append("[]struct{"); + sb.append(generateMessageLengthArgs(thisGroup, indent + INDENT, true)[0]); sb.append("}"); } @@ -3118,13 +3112,17 @@ private Object[] generateMessageLengthArgs( if (count > 0) { - sb.append(",\n").append(indent); + if (inStruct) + { + sb.append("; ").append(indent); + } + else + { + sb.append(",\n").append(indent); + } } - if (withName) - { - sb.append(" ").append(formatPropertyName(varDataToken.name())).append("Length").append(" int"); - } + sb.append(" ").append(formatPropertyName(varDataToken.name())).append("Length").append(" int"); count += 1; @@ -3141,7 +3139,7 @@ private Object[] generateMessageLengthArgs( } - private Object[] generateMessageLengthArgs(final List tokens, final String indent, final boolean withName) + private Object[] generateMessageLengthArgs(final List tokens, final String indent, final boolean inStruct) { int i = 0; @@ -3164,7 +3162,7 @@ private Object[] generateMessageLengthArgs(final List tokens, final Strin final List varData = new ArrayList<>(); collectVarData(tokens, i, varData); - return generateMessageLengthArgs(groups, varData, indent, withName); + return generateMessageLengthArgs(groups, varData, indent, inStruct); } private boolean isMessageConstLength(final List tokens) @@ -3174,6 +3172,94 @@ private boolean isMessageConstLength(final List tokens) return count == 0; } + private String generateMessageLengthCallHelper( + final List groups, + final List varData) + { + final StringBuilder sb = new StringBuilder(); + int count = 0; + + for (int i = 0, size = groups.size(); i < size; i++) + { + final Token groupToken = groups.get(i); + if (groupToken.signal() != Signal.BEGIN_GROUP) + { + throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken); + } + + final int endSignal = findEndSignal(groups, i, Signal.END_GROUP, groupToken.name()); + final String groupName = formatPropertyName(groupToken.name()); + + if (count > 0) + { + sb.append(", "); + } + + final List thisGroup = groups.subList(i, endSignal + 1); + + if (isMessageConstLength(thisGroup)) + { + sb.append("e.").append(groupName).append("Length"); + } + else + { + sb.append("e.").append(groupName).append("ItemLengths"); + } + + count += 1; + + i = endSignal; + } + + for (int i = 0, size = varData.size(); i < size; ) + { + final Token varDataToken = varData.get(i); + if (varDataToken.signal() != Signal.BEGIN_VAR_DATA) + { + throw new IllegalStateException("tokens must begin with BEGIN_VAR_DATA: token=" + varDataToken); + } + + if (count > 0) + { + sb.append(", "); + } + + sb.append("e.").append(formatPropertyName(varDataToken.name())).append("Length"); + + count += 1; + + i += varDataToken.componentTokenCount(); + } + + return sb.toString(); + } + + private CharSequence generateMessageLengthCallHelper(final List tokens) + { + int i = 0; + + final Token groupToken = tokens.get(i); + if (groupToken.signal() != Signal.BEGIN_GROUP) + { + throw new IllegalStateException("tokens must begin with BEGIN_GROUP: token=" + groupToken); + } + + ++i; + final int groupHeaderTokenCount = tokens.get(i).componentTokenCount(); + i += groupHeaderTokenCount; + + final List fields = new ArrayList<>(); + i = collectFields(tokens, i, fields); + + final List groups = new ArrayList<>(); + i = collectGroups(tokens, i, groups); + + final List varData = new ArrayList<>(); + collectVarData(tokens, i, varData); + + return generateMessageLengthCallHelper(groups, varData); + } + private CharSequence generateMessageLength( final List groups, final List varData, @@ -3199,8 +3285,9 @@ private CharSequence generateMessageLength( final long minCount = numInGroupToken.encoding().applicableMinValue().longValue(); final long maxCount = numInGroupToken.encoding().applicableMaxValue().longValue(); - final String countName = formatPropertyName(groupToken.name()) + - (isMessageConstLength(thisGroup) ? "Length" : "len(ItemLengths)"); + final String countName = isMessageConstLength(thisGroup) ? + formatPropertyName(groupToken.name()) + "Length" : + "len(" + formatPropertyName(groupToken.name()) + "ItemLengths)"; final String minCheck = minCount > 0 ? countName + " < " + minCount + " || " : ""; final String maxCheck = countName + " > " + maxCount; @@ -3230,13 +3317,18 @@ private CharSequence generateMessageLength( indent + " panic(\"%5$s outside of allowed range [E110]\")\n" + indent + " }\n\n" + indent + " for _, e := range %1$sItemLengths {\n" + - indent + " length += %2$s.ComputeLength(0)\n" + + indent + " l, err := m.%1$s().ComputeLength(%6$s)\n" + + indent + " if err != nil {\n" + + indent + " return 0, err\n" + + indent + " }\n" + + indent + " length += uint64(l)\n" + indent + " }\n", formatPropertyName(groupToken.name()), formatClassName(groupToken.name()), minCheck, maxCheck, - countName); + countName, + generateMessageLengthCallHelper(thisGroup)); } new Formatter(sbSkip).format( @@ -3287,11 +3379,11 @@ private CharSequence generateMessageLength( indent + "}\n\n" + indent + "func (m *%2$s) ComputeLength(%1$s) (int, error) {\n" + - indent + " length := m.SbeBlockLength()\n" + + indent + " length := uint64(m.SbeBlockLength())\n" + sbEncode + "\n" + indent + " return int(length), nil\n" + indent + "}\n", - generateMessageLengthArgs(groups, varData, indent + INDENT, true)[0], + generateMessageLengthArgs(groups, varData, indent + INDENT, false)[0], className); return sb; From 96d1ae8d1ee52e3539ec3ad82c6580cf869e17c1 Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Sat, 12 Aug 2023 13:43:25 -0400 Subject: [PATCH 09/21] [Go] Fix some compiler warnings and remove unused code --- .../flyweight/GolangFlyweightGenerator.java | 79 ++----------------- 1 file changed, 8 insertions(+), 71 deletions(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java index b674528de7..e85b092216 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java @@ -112,11 +112,6 @@ private static void generateGroupClassHeader( switch (encodingToken.signal()) { case BEGIN_SET: - new Formatter(sb).format(indent + " _%1$s %2$s\n", - propertyName, - typeName); - break; - case BEGIN_COMPOSITE: new Formatter(sb).format(indent + " _%1$s %2$s\n", propertyName, @@ -407,21 +402,6 @@ private static CharSequence generateStringNotPresentCondition( sinceVersion); } - private static CharSequence generateStringViewNotPresentCondition( - final int sinceVersion, final String indent) - { - if (0 == sinceVersion) - { - return ""; - } - - return String.format( - indent + " if m.actingVersion < %1$d {\n" + - indent + " return \"\"\n" + - indent + " }\n\n", - sinceVersion); - } - private static CharSequence generateTypeFieldNotPresentCondition( final int sinceVersion, final String indent) { @@ -586,10 +566,8 @@ public void generateMessageHeaderStub() throws IOException generateComposite(ir.headerStructure().tokens()); } - private List generateTypeStubs() throws IOException + private void generateTypeStubs() throws IOException { - final List typesToInclude = new ArrayList<>(); - for (final List tokens : ir.types()) { switch (tokens.get(0).signal()) @@ -609,33 +587,7 @@ private List generateTypeStubs() throws IOException default: break; } - - typesToInclude.add(tokens.get(0).applicableTypeName()); - } - - return typesToInclude; - } - - private List generateTypesToIncludes(final List tokens) - { - final List typesToInclude = new ArrayList<>(); - - for (final Token token : tokens) - { - switch (token.signal()) - { - case BEGIN_ENUM: - case BEGIN_SET: - case BEGIN_COMPOSITE: - typesToInclude.add(token.applicableTypeName()); - break; - - default: - break; - } } - - return typesToInclude; } /** @@ -647,7 +599,7 @@ public void generate() throws IOException generateUtils(ir.namespaces()); generateMessageHeaderStub(); - final List typesToInclude = generateTypeStubs(); + generateTypeStubs(); for (final List tokens : ir.messages()) { @@ -677,7 +629,7 @@ public void generate() throws IOException final StringBuilder sb = new StringBuilder(); generateFields(sb, className, fields, BASE_INDENT); - generateGroups(sb, groups, BASE_INDENT, className, className); + generateGroups(sb, groups, BASE_INDENT, className); generateVarData(sb, className, varData, BASE_INDENT); generateDisplay(sb, msgToken.name(), className, fields, groups, varData); sb.append(generateMessageLength(groups, varData, BASE_INDENT, className)); @@ -1040,8 +992,7 @@ private void generateGroups( final StringBuilder sb, final List tokens, final String indent, - final String outerClassName, - final String className) + final String outerClassName) { for (int i = 0, size = tokens.size(); i < size; i++) { @@ -1072,7 +1023,7 @@ private void generateGroups( indent + INDENT, fields, groups); generateFields(sb, groupClassName, fields, indent + INDENT); - generateGroups(sb, groups, indent + INDENT, groupClassName, className); + generateGroups(sb, groups, indent + INDENT, groupClassName); final List varData = new ArrayList<>(); i = collectVarData(tokens, i, varData); @@ -1355,7 +1306,7 @@ private void generateEnum(final List tokens) throws IOException out.append("\n"); - fileOut.append(generateEnumFileHeader(ir.namespaces(), enumName)); + fileOut.append(generateEnumFileHeader(ir.namespaces())); fileOut.append(out); } } @@ -1430,9 +1381,7 @@ private CharSequence generateChoices(final String bitsetClassName, final List Date: Sun, 13 Aug 2023 23:15:33 -0400 Subject: [PATCH 10/21] [Go] Draft of OTF decoder --- .gitignore | 1 + gocode/src/flyweight/otf/go.mod | 10 + gocode/src/flyweight/otf/go.sum | 18 + gocode/src/flyweight/otf/otf_test.go | 465 +++++++++++++++++ sbe-tool/src/main/golang/.gitignore | 1 + sbe-tool/src/main/golang/go.mod | 3 + sbe-tool/src/main/golang/main.go | 3 + sbe-tool/src/main/golang/otf/encoding.go | 466 ++++++++++++++++++ sbe-tool/src/main/golang/otf/irdecoder.go | 233 +++++++++ .../src/main/golang/otf/otfheaderdecoder.go | 105 ++++ .../src/main/golang/otf/otfmessagedecoder.go | 311 ++++++++++++ sbe-tool/src/main/golang/otf/token.go | 149 ++++++ 12 files changed, 1765 insertions(+) create mode 100644 gocode/src/flyweight/otf/go.mod create mode 100644 gocode/src/flyweight/otf/go.sum create mode 100644 gocode/src/flyweight/otf/otf_test.go create mode 100644 sbe-tool/src/main/golang/.gitignore create mode 100644 sbe-tool/src/main/golang/go.mod create mode 100644 sbe-tool/src/main/golang/main.go create mode 100644 sbe-tool/src/main/golang/otf/encoding.go create mode 100644 sbe-tool/src/main/golang/otf/irdecoder.go create mode 100644 sbe-tool/src/main/golang/otf/otfheaderdecoder.go create mode 100644 sbe-tool/src/main/golang/otf/otfmessagedecoder.go create mode 100644 sbe-tool/src/main/golang/otf/token.go diff --git a/.gitignore b/.gitignore index 9c76e676a6..834c7b6a7b 100644 --- a/.gitignore +++ b/.gitignore @@ -115,6 +115,7 @@ gocode/src/flyweight/example-schema/cpu.out gocode/src/flyweight/example-socket-clientserver/example-socket-clientserver gocode/src/flyweight/extension gocode/src/flyweight/extension2 +gocode/src/flyweight/otf/*.sbeir # csharp csharp/*/bin diff --git a/gocode/src/flyweight/otf/go.mod b/gocode/src/flyweight/otf/go.mod new file mode 100644 index 0000000000..fbe2902e53 --- /dev/null +++ b/gocode/src/flyweight/otf/go.mod @@ -0,0 +1,10 @@ +module github.com/real-logic/simple-binary-encoding/otf/test + +require ( + github.com/real-logic/simple-binary-encoding v0.0.0-00010101000000-000000000000 + github.com/stretchr/testify v1.8.4 +) + +replace github.com/real-logic/simple-binary-encoding => ../../../../sbe-tool/src/main/golang + +go 1.12 diff --git a/gocode/src/flyweight/otf/go.sum b/gocode/src/flyweight/otf/go.sum new file mode 100644 index 0000000000..673e9186f2 --- /dev/null +++ b/gocode/src/flyweight/otf/go.sum @@ -0,0 +1,18 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/gocode/src/flyweight/otf/otf_test.go b/gocode/src/flyweight/otf/otf_test.go new file mode 100644 index 0000000000..a35f819e22 --- /dev/null +++ b/gocode/src/flyweight/otf/otf_test.go @@ -0,0 +1,465 @@ +package otf + +import ( + "fmt" + "github.com/real-logic/simple-binary-encoding/otf" + "github.com/real-logic/simple-binary-encoding/otf/test/code_generation_test" + "github.com/stretchr/testify/assert" + "strconv" + "strings" + "testing" +) + +//go:generate java -Dsbe.output.dir=. -Dsbe.target.language=golang -Dsbe.go.generate.generate.flyweights=true -Dsbe.generate.ir=true -jar ../../../../sbe-all/build/libs/sbe-all-1.30.0-SNAPSHOT.jar ../../../../sbe-tool/src/test/resources/code-generation-schema.xml + +const SchemaFilename = "code-generation-schema.sbeir" +const MsgBufferCapacity = 4 * 1024 + +type TestListener struct { + out strings.Builder + compositeLevel int + namedScope []string +} + +func (t *TestListener) OnBeginMessage(token otf.Token) { + t.namedScope = append(t.namedScope, token.Name()+".") +} + +func (t *TestListener) OnEndMessage(token otf.Token) { + t.namedScope = t.namedScope[:len(t.namedScope)-1] +} + +func (t *TestListener) OnEncoding(fieldToken otf.Token, buffer []byte, typeToken otf.Token, actingVersion uint64) { + value := t.readEncodingAsString(buffer, fieldToken, typeToken, uint64(fieldToken.TokenVersion()), actingVersion) + + t.printScope() + if t.compositeLevel > 0 { + t.out.WriteString(typeToken.Name()) + } else { + t.out.WriteString(fieldToken.Name()) + } + t.out.WriteString("=") + t.out.WriteString(value) + t.out.WriteString("\n") + +} + +func (t *TestListener) printScope() { + for i := range t.namedScope { + t.out.WriteString(t.namedScope[i]) + } +} + +func (t *TestListener) determineName( + thresholdLevel int, + fieldToken otf.Token, + tokens []otf.Token, + fromIndex int, +) string { + if t.compositeLevel > thresholdLevel { + return tokens[fromIndex].Name() + } else { + return fieldToken.Name() + } +} + +func (t *TestListener) readEncodingAsString( + buffer []byte, + fieldToken otf.Token, + typeToken otf.Token, + fieldVersion uint64, + actingVersion uint64, +) string { + constOrNotPresentValue := t.constOrNotPresentValue(typeToken, fieldVersion, actingVersion) + if constOrNotPresentValue.PrimitiveType != otf.NONE { + encoding := typeToken.Encoding() + characterEncoding := encoding.CharacterEncoding() + if constOrNotPresentValue.Size() == 1 && characterEncoding == "" { + // TODO java uses this + // final byte[] bytes = { (byte)constOrNotPresentValue.longValue() }; + // return new String(bytes, characterEncoding); + return constOrNotPresentValue.String() + } else { + value := constOrNotPresentValue.String() + size := typeToken.ArrayLength() + if size < 2 { + return value + } + sb := strings.Builder{} + more := false + for i := int32(0); i < size; i++ { + if more { + sb.WriteString(", ") + more = true + } + sb.WriteString(value) + } + return sb.String() + } + } + + sb := strings.Builder{} + encoding := typeToken.Encoding() + elementSize := int32(encoding.PrimitiveType().Size()) + + more := false + for i := int32(0); i < typeToken.ArrayLength(); i++ { + if more { + sb.WriteString(", ") + more = true + } + arrayValue := otf.NewPrimitiveValue(encoding.PrimitiveType(), buffer[i*elementSize:(i+1)*elementSize]) + sb.WriteString(arrayValue.String()) + } + return sb.String() +} + +func (t *TestListener) readEncodingAsInt( + buffer []byte, + typeToken otf.Token, + fieldVersion uint64, + actingVersion uint64, +) int64 { + constOrNotPresentValue := t.constOrNotPresentValue(typeToken, fieldVersion, actingVersion) + if constOrNotPresentValue.PrimitiveType != otf.NONE { + return constOrNotPresentValue.AsInt() + } + typeTokenEncoding := typeToken.Encoding() + v, _ := otf.GetAsInt(typeTokenEncoding.PrimitiveType(), typeTokenEncoding.ByteOrder(), buffer) + return v +} + +func (t *TestListener) readEncodingAsUInt( + buffer []byte, + typeToken otf.Token, + fieldVersion uint64, + actingVersion uint64, +) uint64 { + constOrNotPresentValue := t.constOrNotPresentValue(typeToken, fieldVersion, actingVersion) + if constOrNotPresentValue.PrimitiveType != otf.NONE { + return constOrNotPresentValue.AsUInt() + } + typeTokenEncoding := typeToken.Encoding() + v, _ := otf.GetAsUInt(typeTokenEncoding.PrimitiveType(), typeTokenEncoding.ByteOrder(), buffer) + return v +} + +func (t *TestListener) constOrNotPresentValue( + typeToken otf.Token, + fieldVersion uint64, + actingVersion uint64, +) otf.PrimitiveValue { + typeTokenEncoding := typeToken.Encoding() + if typeToken.IsConstantEncoding() { + return typeTokenEncoding.ConstValue() + } else if typeToken.IsOptionalEncoding() && actingVersion < fieldVersion { + return typeTokenEncoding.ApplicableNullValue() + } else { + return otf.PrimitiveValue{} + } +} + +func (t *TestListener) OnEnum( + fieldToken otf.Token, + buffer []byte, + tokens []otf.Token, + fromIndex int, + toIndex int, + actingVersion uint64, +) { + typeToken := tokens[fromIndex+1] + encoding := typeToken.Encoding() + + value := "" + if fieldToken.IsConstantEncoding() { + encoding := fieldToken.Encoding() + refValue := encoding.ConstValue() + indexOfDot := strings.LastIndex(refValue.String(), ".") + if indexOfDot == -1 { + value = refValue.String() + } else { + value = refValue.String()[indexOfDot+1:] + } + } else if encoding.PrimitiveType().IsUnsigned() { + encodedValue := t.readEncodingAsUInt( + buffer, + typeToken, + uint64(fieldToken.TokenVersion()), + actingVersion, + ) + for i := fromIndex + 1; i < toIndex; i++ { + encoding := tokens[i].Encoding() + constValue := encoding.ConstValue() + if constValue.AsUInt() == encodedValue { + value = tokens[i].Name() + break + } + } + } else { + encodedValue := t.readEncodingAsInt( + buffer, + typeToken, + uint64(fieldToken.TokenVersion()), + actingVersion, + ) + for i := fromIndex + 1; i < toIndex; i++ { + encoding := tokens[i].Encoding() + constValue := encoding.ConstValue() + if constValue.AsInt() == encodedValue { + value = tokens[i].Name() + break + } + } + } + + t.printScope() + t.out.WriteString(t.determineName(0, fieldToken, tokens, fromIndex)) + t.out.WriteString("=") + t.out.WriteString(value) + t.out.WriteString("\n") +} + +func (t *TestListener) OnBitSet( + fieldToken otf.Token, + buffer []byte, + tokens []otf.Token, + fromIndex int, + toIndex int, + actingVersion uint64, +) { + typeToken := tokens[fromIndex+1] + encodedValue := t.readEncodingAsInt( + buffer, + typeToken, + uint64(fieldToken.TokenVersion()), + actingVersion, + ) + t.printScope() + t.out.WriteString(t.determineName(0, fieldToken, tokens, fromIndex)) + t.out.WriteString(":") + for i := fromIndex + 1; i < toIndex; i++ { + t.out.WriteString(" ") + t.out.WriteString(tokens[i].Name()) + t.out.WriteString("=") + encoding := tokens[i].Encoding() + constValue := encoding.ConstValue() + bitPosition := constValue.AsInt() + flag := (encodedValue & (1 << uint64(bitPosition))) != 0 + t.out.WriteString(strconv.FormatBool(flag)) + } + t.out.WriteString("\n") +} + +func (t *TestListener) OnBeginComposite( + fieldToken otf.Token, + tokens []otf.Token, + fromIndex int, + toIndex int, +) { + t.compositeLevel++ + t.namedScope = append(t.namedScope, t.determineName(1, fieldToken, tokens, fromIndex)+".") +} + +func (t *TestListener) OnEndComposite( + token otf.Token, + tokens []otf.Token, + fromIndex int, + toIndex int, +) { + t.compositeLevel-- + t.namedScope = t.namedScope[:len(t.namedScope)-1] +} + +func (t *TestListener) OnGroupHeader( + token otf.Token, + numInGroup uint64, +) { + t.printScope() + t.out.WriteString(token.Name()) + t.out.WriteString(" Group Header : numInGroup=") + t.out.WriteString(strconv.FormatUint(numInGroup, 10)) + t.out.WriteString("\n") +} + +func (t *TestListener) OnBeginGroup( + token otf.Token, + groupIndex uint64, + numInGroup uint64, +) { + t.namedScope = append(t.namedScope, token.Name()+".") +} + +func (t *TestListener) OnEndGroup( + token otf.Token, + groupIndex uint64, + numInGroup uint64, +) { + t.namedScope = t.namedScope[:len(t.namedScope)-1] +} + +func (t *TestListener) OnVarData( + fieldToken otf.Token, + buffer []byte, + length uint64, + typeToken otf.Token, +) { + value := "" + typeTokenEncoding := typeToken.Encoding() + if characterEncoding := typeTokenEncoding.CharacterEncoding(); characterEncoding == "" { + value = fmt.Sprintf("%d bytes of raw data", length) + } else { + value = string(buffer[:length]) + } + + t.printScope() + t.out.WriteString(fieldToken.Name()) + t.out.WriteString("=") + t.out.WriteString(value) + t.out.WriteString("\n") +} + +func TestOtf(t *testing.T) { + assert := assert.New(t) + + ir := otf.NewIrDecoder() + if ir.DecodeFile(SchemaFilename) < 0 { + t.Errorf("Error decoding %s", SchemaFilename) + } + + var car code_generation_test.Car + buffer := make([]byte, MsgBufferCapacity) + encodedLength := encodeCar(&car, buffer) + + headerDecoder, _ := otf.NewOtfHeaderDecoder(ir.Header()) + bufferOffset := 0 + templateId, _ := headerDecoder.TemplateId(buffer[bufferOffset:]) + schemaId, _ := headerDecoder.SchemaId(buffer[bufferOffset:]) + actingVersion, _ := headerDecoder.SchemaVersion(buffer[bufferOffset:]) + blockLength, _ := headerDecoder.BlockLength(buffer[bufferOffset:]) + + if uint16(schemaId) != car.SbeSchemaId() { + t.Errorf("Invalid schema id: %d", schemaId) + } + + bufferOffset += int(headerDecoder.EncodedLength()) + listener := &TestListener{} + tokens := ir.MessageByID(int32(templateId)) + bufferOffset = otf.Decode( + buffer[bufferOffset:], + actingVersion, + blockLength, + tokens, + listener, + ) + + assert.EqualValues(bufferOffset, encodedLength, "Message not fully decoded") + expected := `Car.serialNumber=1234 +Car.modelYear=2013 +Car.available=T +Car.code=A +Car.someNumbers=01234 +Car.vehicleCode=abcdef +Car.extras: sunRoof=false sportsPack=false cruiseControl=false +Car.discountedModel=C +Car.engine.capacity=2000 +Car.engine.numCylinders=4 +Car.engine.maxRpm=9000 +Car.engine.manufacturerCode=123 +Car.engine.fuel=Petrol +Car.engine.booster.BoostType=NITROUS +Car.engine.booster.horsePower=200 +Car.fuelFigures Group Header : numInGroup=3 +Car.fuelFigures.speed=30 +Car.fuelFigures.mpg=35.900001525878906 +Car.fuelFigures.usageDescription=Urban Cycle +Car.fuelFigures.speed=55 +Car.fuelFigures.mpg=49 +Car.fuelFigures.usageDescription=Combined Cycle +Car.fuelFigures.speed=75 +Car.fuelFigures.mpg=40 +Car.fuelFigures.usageDescription=Highway Cycle +Car.performanceFigures Group Header : numInGroup=2 +Car.performanceFigures.octaneRating=95 +Car.performanceFigures.acceleration Group Header : numInGroup=3 +Car.performanceFigures.acceleration.mph=30 +Car.performanceFigures.acceleration.seconds=4 +Car.performanceFigures.acceleration.mph=60 +Car.performanceFigures.acceleration.seconds=7.5 +Car.performanceFigures.acceleration.mph=100 +Car.performanceFigures.acceleration.seconds=12.199999809265137 +Car.performanceFigures.octaneRating=99 +Car.performanceFigures.acceleration Group Header : numInGroup=3 +Car.performanceFigures.acceleration.mph=30 +Car.performanceFigures.acceleration.seconds=3.799999952316284 +Car.performanceFigures.acceleration.mph=60 +Car.performanceFigures.acceleration.seconds=7.099999904632568 +Car.performanceFigures.acceleration.mph=100 +Car.performanceFigures.acceleration.seconds=11.800000190734863 +Car.manufacturer=Honda +Car.model=Civic VTi +Car.activationCode=abcdef +Car.color= +` + assert.EqualValues(expected, listener.out.String()) +} + +func encodeCar(car *code_generation_test.Car, buffer []byte) int { + const vehicleCode = "abcdef" + const manufacturerCode = "123" + const activationCode = "abcdef" + const manufacturer = "Honda" + const model = "Civic VTi" + + car.WrapAndApplyHeader(buffer, 0, uint64(len(buffer))). + SetSerialNumber(1234). + SetModelYear(2013). + SetAvailable(code_generation_test.BooleanType_T). + SetCode(code_generation_test.Model_A). + SetVehicleCode(vehicleCode) + + // change this to a loop + for i := 0; i < car.SomeNumbersLength(); i++ { + car.SetSomeNumbersIndex(uint64(i), int32(i)) + } + + car.Extras(). + Clear(). + SetCruiseControl(true). + SetSportsPack(true). + SetSunRoof(false) + + car.Engine(). + SetCapacity(2000). + SetNumCylinders(4). + SetManufacturerCode(manufacturerCode). + Booster(). + SetBoostType(code_generation_test.BoostType_NITROUS). + SetHorsePower(200) + + car.FuelFiguresCount(3). + Next().SetSpeed(30).SetMpg(35.9).PutUsageDescription("Urban Cycle"). + Next().SetSpeed(55).SetMpg(49.0).PutUsageDescription("Combined Cycle"). + Next().SetSpeed(75).SetMpg(40.0).PutUsageDescription("Highway Cycle") + + figures := car.PerformanceFiguresCount(2) + figures.Next(). + SetOctaneRating(95). + AccelerationCount(3). + Next().SetMph(30).SetSeconds(4.0). + Next().SetMph(60).SetSeconds(7.5). + Next().SetMph(100).SetSeconds(12.2) + figures.Next(). + SetOctaneRating(99). + AccelerationCount(3). + Next().SetMph(30).SetSeconds(3.8). + Next().SetMph(60).SetSeconds(7.1). + Next().SetMph(100).SetSeconds(11.8) + + car.PutManufacturer(manufacturer). + PutModel(model). + PutActivationCode(activationCode). + PutColor("") + + // var hdr code_generation_test.MessageHeader + return int(car.EncodedLength()) +} diff --git a/sbe-tool/src/main/golang/.gitignore b/sbe-tool/src/main/golang/.gitignore new file mode 100644 index 0000000000..aafd6f4184 --- /dev/null +++ b/sbe-tool/src/main/golang/.gitignore @@ -0,0 +1 @@ +uk_co_real_logic_sbe_ir_generated/* diff --git a/sbe-tool/src/main/golang/go.mod b/sbe-tool/src/main/golang/go.mod new file mode 100644 index 0000000000..0a6dd9e719 --- /dev/null +++ b/sbe-tool/src/main/golang/go.mod @@ -0,0 +1,3 @@ +module github.com/real-logic/simple-binary-encoding + +go 1.12 diff --git a/sbe-tool/src/main/golang/main.go b/sbe-tool/src/main/golang/main.go new file mode 100644 index 0000000000..3b246a530c --- /dev/null +++ b/sbe-tool/src/main/golang/main.go @@ -0,0 +1,3 @@ +package sbe + +//go:generate java -Dsbe.output.dir=. -Dsbe.target.language=golang -Dsbe.go.generate.generate.flyweights=true -jar ${SBE_JAR} ../resources/sbe-ir.xml diff --git a/sbe-tool/src/main/golang/otf/encoding.go b/sbe-tool/src/main/golang/otf/encoding.go new file mode 100644 index 0000000000..a74f64ffda --- /dev/null +++ b/sbe-tool/src/main/golang/otf/encoding.go @@ -0,0 +1,466 @@ +// Copyright 2013-2023 Real Logic Limited. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package otf + +import ( + "encoding/binary" + "errors" + "fmt" + "math" + "strconv" +) + +type ByteOrder int + +const ( + SbeLittleEndian ByteOrder = iota + SbeBigEndian +) + +// String returns the string representation of the ByteOrder. +func (o ByteOrder) String() string { + switch o { + case SbeLittleEndian: + return "SbeLittleEndian" + case SbeBigEndian: + return "SbeBigEndian" + default: + return "Unknown" + } +} + +type PrimitiveType int + +const ( + NONE PrimitiveType = iota + CHAR + INT8 + INT16 + INT32 + INT64 + UINT8 + UINT16 + UINT32 + UINT64 + FLOAT + DOUBLE +) + +func (p PrimitiveType) String() string { + switch p { + case NONE: + return "NONE" + case CHAR: + return "CHAR" + case INT8: + return "INT8" + case INT16: + return "INT16" + case INT32: + return "INT32" + case INT64: + return "INT64" + case UINT8: + return "UINT8" + case UINT16: + return "UINT16" + case UINT32: + return "UINT32" + case UINT64: + return "UINT64" + case FLOAT: + return "FLOAT" + case DOUBLE: + return "DOUBLE" + default: + return "Unknown" + } +} + +func (p PrimitiveType) Size() int { + switch p { + case CHAR, INT8, UINT8: + return 1 + case INT16, UINT16: + return 2 + case INT32, UINT32, FLOAT: + return 4 + case INT64, UINT64, DOUBLE: + return 8 + default: + return 0 + } +} + +// IsUnsigned returns true if the type is unsigned. +func (p PrimitiveType) IsUnsigned() bool { + switch p { + case UINT8, UINT16, UINT32, UINT64: + return true + default: + return false + } +} + +type Presence int + +const ( + SbeRequired Presence = iota + SbeOptional + SbeConstant +) + +func (p Presence) String() string { + switch p { + case SbeRequired: + return "SbeRequired" + case SbeOptional: + return "SbeOptional" + case SbeConstant: + return "SbeConstant" + default: + return "Unknown" + } +} + +type PrimitiveValue struct { + PrimitiveType + size int + asInt int64 + asUInt uint64 + asDouble float64 + arrayValue string +} + +// String returns the string representation of the value. +func (p *PrimitiveValue) String() string { + switch p.PrimitiveType { + case CHAR: + if p.size > 1 { + return p.arrayValue + } + return string(byte(p.asInt)) + case INT8, INT16, INT32, INT64: + return strconv.FormatInt(p.asInt, 10) + case UINT8, UINT16, UINT32, UINT64: + return strconv.FormatUint(p.asUInt, 10) + case FLOAT, DOUBLE: + return strconv.FormatFloat(p.asDouble, 'f', -1, 64) + default: + return "" + } +} + +func NewPrimitiveValue(t PrimitiveType, value []byte) PrimitiveValue { + p := PrimitiveValue{PrimitiveType: t} + + if len(value) == 0 { + return p + } + + switch t { + case CHAR: + if len(value) > 1 { + p.arrayValue = string(value) + p.size = len(value) + } else { + p.asInt = int64(value[0]) + p.size = 1 + } + case INT8: + p.asInt = int64(value[0]) + p.size = 1 + case INT16: + p.asInt = int64(binary.LittleEndian.Uint16(value)) + p.size = 2 + case INT32: + p.asInt = int64(binary.LittleEndian.Uint32(value)) + p.size = 4 + case INT64: + p.asInt = int64(binary.LittleEndian.Uint64(value)) + p.size = 8 + case UINT8: + p.asUInt = uint64(value[0]) + p.size = 1 + case UINT16: + p.asUInt = uint64(binary.LittleEndian.Uint16(value)) + p.size = 2 + case UINT32: + p.asUInt = uint64(binary.LittleEndian.Uint32(value)) + p.size = 4 + case UINT64: + p.asUInt = binary.LittleEndian.Uint64(value) + p.size = 8 + case FLOAT: + p.asDouble = float64(math.Float32frombits(binary.LittleEndian.Uint32(value))) + p.size = 4 + case DOUBLE: + p.asDouble = math.Float64frombits(binary.LittleEndian.Uint64(value)) + p.size = 8 + default: + p.PrimitiveType = NONE + p.size = 0 + } + + return p +} + +func (p *PrimitiveValue) AsInt() int64 { + return p.asInt +} + +func (p *PrimitiveValue) AsUInt() uint64 { + return p.asUInt +} + +func (p *PrimitiveValue) AsDouble() float64 { + return p.asDouble +} + +func (p *PrimitiveValue) GetArray() string { + return p.arrayValue +} + +func (p *PrimitiveValue) Size() int { + return p.size +} + +type Encoding struct { + presence Presence + primitiveType PrimitiveType + byteOrder ByteOrder + minValue PrimitiveValue + maxValue PrimitiveValue + nullValue PrimitiveValue + constValue PrimitiveValue + characterEncoding string + epoch string + timeUnit string + semanticType string +} + +func (e *Encoding) String() string { + return fmt.Sprintf( + "Encoding{presence: %s primitiveType: %s, byteOrder: %s, minValue: %s, maxValue: %s, nullValue: %s, constValue: %s, characterEncoding: %s, epoch: %s, timeUnit: %s, semanticType: %s}", + e.presence, + e.primitiveType, + e.byteOrder, + &e.minValue, + &e.maxValue, + &e.nullValue, + &e.constValue, + e.characterEncoding, + e.epoch, + e.timeUnit, + e.semanticType, + ) +} + +func NewEncoding( + primitiveType PrimitiveType, + presence Presence, + byteOrder ByteOrder, + minValue, maxValue, nullValue, constValue PrimitiveValue, + characterEncoding, epoch, timeUnit, semanticType string) Encoding { + return Encoding{ + presence: presence, + primitiveType: primitiveType, + byteOrder: byteOrder, + minValue: minValue, + maxValue: maxValue, + nullValue: nullValue, + constValue: constValue, + characterEncoding: characterEncoding, + epoch: epoch, + timeUnit: timeUnit, + semanticType: semanticType, + } +} + +func GetChar(buffer []byte) byte { + return buffer[0] +} + +func GetInt8(buffer []byte) int8 { + return int8(buffer[0]) +} + +func GetInt16(buffer []byte, order ByteOrder) int16 { + if order == SbeLittleEndian { + return int16(binary.LittleEndian.Uint16(buffer)) + } + return int16(binary.BigEndian.Uint16(buffer)) +} + +func GetInt32(buffer []byte, order ByteOrder) int32 { + if order == SbeLittleEndian { + return int32(binary.LittleEndian.Uint32(buffer)) + } + return int32(binary.BigEndian.Uint32(buffer)) +} + +func GetInt64(buffer []byte, order ByteOrder) int64 { + if order == SbeLittleEndian { + return int64(binary.LittleEndian.Uint64(buffer)) + } + return int64(binary.BigEndian.Uint64(buffer)) +} + +func GetUInt8(buffer []byte) uint8 { + return buffer[0] +} + +func GetUInt16(buffer []byte, order ByteOrder) uint16 { + if order == SbeLittleEndian { + return binary.LittleEndian.Uint16(buffer) + } + return binary.BigEndian.Uint16(buffer) +} + +func GetUInt32(buffer []byte, order ByteOrder) uint32 { + if order == SbeLittleEndian { + return binary.LittleEndian.Uint32(buffer) + } + return binary.BigEndian.Uint32(buffer) +} + +func GetUInt64(buffer []byte, order ByteOrder) uint64 { + if order == SbeLittleEndian { + return binary.LittleEndian.Uint64(buffer) + } + return binary.BigEndian.Uint64(buffer) +} + +func GetFloat(buffer []byte, order ByteOrder) float32 { + if order == SbeLittleEndian { + return math.Float32frombits(binary.LittleEndian.Uint32(buffer)) + } + return math.Float32frombits(binary.BigEndian.Uint32(buffer)) +} + +func GetDouble(buffer []byte, order ByteOrder) float64 { + if order == SbeLittleEndian { + return math.Float64frombits(binary.LittleEndian.Uint64(buffer)) + } + return math.Float64frombits(binary.BigEndian.Uint64(buffer)) +} + +func GetAsInt(primitiveType PrimitiveType, byteOrder ByteOrder, buffer []byte) (int64, error) { + switch primitiveType { + case CHAR: + return int64(GetChar(buffer)), nil + case INT8: + return int64(GetInt8(buffer)), nil + case INT16: + return int64(GetInt16(buffer, byteOrder)), nil + case INT32: + return int64(GetInt32(buffer, byteOrder)), nil + case INT64: + return GetInt64(buffer, byteOrder), nil + default: + return 0, errors.New("incorrect type for Encoding.GetAsInt") + } +} + +func GetAsUInt(primitiveType PrimitiveType, byteOrder ByteOrder, buffer []byte) (uint64, error) { + switch primitiveType { + case UINT8: + return uint64(GetUInt8(buffer)), nil + case UINT16: + return uint64(GetUInt16(buffer, byteOrder)), nil + case UINT32: + return uint64(GetUInt32(buffer, byteOrder)), nil + case UINT64: + return GetUInt64(buffer, byteOrder), nil + default: + return 0, errors.New("incorrect type for Encoding.GetAsUInt") + } +} + +func GetAsDouble(primitiveType PrimitiveType, byteOrder ByteOrder, buffer []byte) (float64, error) { + switch primitiveType { + case FLOAT: + return float64(GetFloat(buffer, byteOrder)), nil + case DOUBLE: + return GetDouble(buffer, byteOrder), nil + default: + return 0, errors.New("incorrect type for Encoding.GetAsDouble") + } +} + +func (e *Encoding) GetAsInt(buffer []byte) (int64, error) { + return GetAsInt(e.primitiveType, e.byteOrder, buffer) +} + +func (e *Encoding) GetAsUInt(buffer []byte) (uint64, error) { + return GetAsUInt(e.primitiveType, e.byteOrder, buffer) +} + +func (e *Encoding) GetAsDouble(buffer []byte) (float64, error) { + return GetAsDouble(e.primitiveType, e.byteOrder, buffer) +} + +func (e *Encoding) Presence() Presence { + return e.presence +} + +func (e *Encoding) PrimitiveType() PrimitiveType { + return e.primitiveType +} + +func (e *Encoding) ByteOrder() ByteOrder { + return e.byteOrder +} + +func (e *Encoding) MinValue() PrimitiveValue { + return e.minValue +} + +func (e *Encoding) MaxValue() PrimitiveValue { + return e.maxValue +} + +func (e *Encoding) NullValue() PrimitiveValue { + return e.nullValue +} + +func (e *Encoding) ConstValue() PrimitiveValue { + return e.constValue +} + +func (e *Encoding) CharacterEncoding() string { + return e.characterEncoding +} + +func (e *Encoding) Epoch() string { + return e.epoch +} + +func (e *Encoding) TimeUnit() string { + return e.timeUnit +} + +func (e *Encoding) SemanticType() string { + return e.semanticType +} + +func (e *Encoding) ApplicableNullValue() PrimitiveValue { + if e.nullValue.PrimitiveType != NONE { + return e.nullValue + } + return PrimitiveValue{ + PrimitiveType: e.primitiveType, + } +} diff --git a/sbe-tool/src/main/golang/otf/irdecoder.go b/sbe-tool/src/main/golang/otf/irdecoder.go new file mode 100644 index 0000000000..ffe58c7a11 --- /dev/null +++ b/sbe-tool/src/main/golang/otf/irdecoder.go @@ -0,0 +1,233 @@ +package otf + +import ( + ir "github.com/real-logic/simple-binary-encoding/uk_co_real_logic_sbe_ir_generated" + "os" +) + +type IrDecoder struct { + headerTokens []Token + messages [][]Token + buffer []byte + length uint64 + id int32 +} + +func NewIrDecoder() *IrDecoder { + return &IrDecoder{} +} + +func (decoder *IrDecoder) Decode(irBuffer []byte) int { + length := uint64(len(irBuffer)) + decoder.length = length + if length == 0 { + return -1 + } + decoder.buffer = make([]byte, length) + copy(decoder.buffer, irBuffer) + return decoder.decodeIr() +} + +func (decoder *IrDecoder) DecodeFile(filename string) int { + fileStat, err := os.Stat(filename) + if err != nil { + return -1 + } + + decoder.length = uint64(fileStat.Size()) + if decoder.length == 0 { + return -1 + } + decoder.buffer = make([]byte, decoder.length) + + if err := readFileIntoBuffer(decoder.buffer, filename, decoder.length); err != nil { + return -1 + } + + return decoder.decodeIr() +} + +func (decoder *IrDecoder) Header() []Token { + return decoder.headerTokens +} + +func (decoder *IrDecoder) Messages() [][]Token { + return decoder.messages +} + +func (decoder *IrDecoder) Message(id int32, version int32) []Token { + for _, tokens := range decoder.messages { + token := &tokens[0] + if token.Signal() == SignalBeginMessage && token.FieldId() == id && token.TokenVersion() == version { + return tokens + } + } + return nil +} + +func (decoder *IrDecoder) MessageByID(id int32) []Token { + for _, tokens := range decoder.messages { + token := &tokens[0] + if token.Signal() == SignalBeginMessage && token.FieldId() == id { + return tokens + } + } + return nil +} + +func (decoder *IrDecoder) decodeIr() int { + var frame ir.FrameCodec + offset := uint64(0) + var tmp [256]byte + + frame.WrapForDecode( + decoder.buffer, + offset, + uint64(frame.SbeBlockLength()), + uint64(frame.SbeSchemaVersion()), + decoder.length, + ) + + frame.GetPackageName(tmp[:]) + + if frame.IrVersion() != 0 { + return -1 + } + + frame.GetNamespaceName(tmp[:]) + frame.GetSemanticVersion(tmp[:]) + + offset += frame.EncodedLength() + + decoder.headerTokens = make([]Token, 0) + + headerLength := decoder.readHeader(offset) + + decoder.id = frame.IrId() + + offset += headerLength + + for offset < decoder.length { + offset += decoder.readMessage(offset) + } + + return 0 +} +func (decoder *IrDecoder) decodeAndAddToken(tokens *[]Token, offset uint64) uint64 { + var tokenCodec ir.TokenCodec + tokenCodec.WrapForDecode( + decoder.buffer, + offset, + uint64(tokenCodec.SbeBlockLength()), + uint64(tokenCodec.SbeSchemaVersion()), + decoder.length, + ) + + signal := Signal(tokenCodec.Signal()) + primitiveType := PrimitiveType(tokenCodec.PrimitiveType()) + presence := Presence(tokenCodec.Presence()) + byteOrder := ByteOrder(tokenCodec.ByteOrder()) + tokenOffset := tokenCodec.TokenOffset() + tokenSize := tokenCodec.TokenSize() + id := tokenCodec.FieldId() + version := tokenCodec.TokenVersion() + componentTokenCount := tokenCodec.ComponentTokenCount() + + name := tokenCodec.GetNameAsString() + + tmpBuffer := [256]byte{} + tmpLength := tokenCodec.GetConstValue(tmpBuffer[:]) + constValue := NewPrimitiveValue(primitiveType, tmpBuffer[:tmpLength]) + tmpLength = tokenCodec.GetMinValue(tmpBuffer[:]) + minValue := NewPrimitiveValue(primitiveType, tmpBuffer[:tmpLength]) + tmpLength = tokenCodec.GetMaxValue(tmpBuffer[:]) + maxValue := NewPrimitiveValue(primitiveType, tmpBuffer[:tmpLength]) + tmpLength = tokenCodec.GetNullValue(tmpBuffer[:]) + nullValue := NewPrimitiveValue(primitiveType, tmpBuffer[:tmpLength]) + + characterEncoding := tokenCodec.GetCharacterEncodingAsString() + epoch := tokenCodec.GetEpochAsString() + timeUnit := tokenCodec.GetTimeUnitAsString() + semanticType := tokenCodec.GetSemanticTypeAsString() + description := tokenCodec.GetDescriptionAsString() + tokenCodec.GetReferencedNameAsString() + + encoding := NewEncoding( + primitiveType, + presence, + byteOrder, + minValue, + maxValue, + nullValue, + constValue, + characterEncoding, + epoch, + timeUnit, + semanticType, + ) + + token := Token{ + offset: tokenOffset, + fieldId: id, + version: version, + encodedLength: tokenSize, + componentTokenCount: componentTokenCount, + signal: signal, + name: name, + description: description, + encoding: encoding, + } + + *tokens = append(*tokens, token) + + return tokenCodec.EncodedLength() +} + +func (decoder *IrDecoder) readHeader(offset uint64) uint64 { + size := uint64(0) + for offset+size < decoder.length { + size += decoder.decodeAndAddToken(&decoder.headerTokens, offset+size) + token := &decoder.headerTokens[len(decoder.headerTokens)-1] + if token.signal == SignalEndComposite { + break + } + } + return size +} + +func (decoder *IrDecoder) readMessage(offset uint64) uint64 { + size := uint64(0) + tokensForMessage := make([]Token, 0) + for offset+size < decoder.length { + size += decoder.decodeAndAddToken(&tokensForMessage, offset+size) + token := &tokensForMessage[len(tokensForMessage)-1] + if token.signal == SignalEndMessage { + break + } + } + decoder.messages = append(decoder.messages, tokensForMessage) + return size +} + +func readFileIntoBuffer(buffer []byte, filename string, length uint64) error { + f, err := os.Open(filename) + if err != nil { + return err + } + defer f.Close() + + var remaining = length + for remaining > 0 { + bytes := uint(4098) + if remaining < 4098 { + bytes = uint(remaining) + } + sz, err := f.Read(buffer[length-remaining : length-remaining+uint64(bytes)]) + if err != nil { + return err + } + remaining -= uint64(sz) + } + + return nil +} diff --git a/sbe-tool/src/main/golang/otf/otfheaderdecoder.go b/sbe-tool/src/main/golang/otf/otfheaderdecoder.go new file mode 100644 index 0000000000..5905437fa1 --- /dev/null +++ b/sbe-tool/src/main/golang/otf/otfheaderdecoder.go @@ -0,0 +1,105 @@ +package otf + +import "fmt" + +type OftHeaderDecoder struct { + encodedLength int32 + blockLengthOffset int32 + templateIdOffset int32 + schemaIdOffset int32 + schemaVersionOffset int32 + blockLengthType PrimitiveType + templateIdType PrimitiveType + schemaIdType PrimitiveType + schemaVersionType PrimitiveType + blockLengthByteOrder ByteOrder + templateIdByteOrder ByteOrder + schemaIdByteOrder ByteOrder + schemaVersionByteOrder ByteOrder +} + +func NewOtfHeaderDecoder(tokens []Token) (*OftHeaderDecoder, error) { + decoder := &OftHeaderDecoder{ + encodedLength: tokens[0].EncodedLength(), + } + + var blockLengthToken, + templateIdToken, + schemaIdToken, + versionToken *Token + + for i := range tokens { + token := tokens[i] + name := token.Name() + switch name { + case "blockLength": + blockLengthToken = &token + case "templateId": + templateIdToken = &token + case "schemaId": + schemaIdToken = &token + case "version": + versionToken = &token + } + } + + if blockLengthToken == nil { + return nil, fmt.Errorf("blockLength token not found") + } + + decoder.blockLengthOffset = blockLengthToken.Offset() + encoding := blockLengthToken.Encoding() + decoder.blockLengthType = encoding.PrimitiveType() + decoder.blockLengthByteOrder = encoding.ByteOrder() + + if templateIdToken == nil { + return nil, fmt.Errorf("templateId token not found") + } + + decoder.templateIdOffset = templateIdToken.Offset() + templateIdEncoding := templateIdToken.Encoding() + decoder.templateIdType = templateIdEncoding.PrimitiveType() + decoder.templateIdByteOrder = templateIdEncoding.ByteOrder() + + if schemaIdToken == nil { + return nil, fmt.Errorf("schemaId token not found") + } + + decoder.schemaIdOffset = schemaIdToken.Offset() + schemaIdEncoding := schemaIdToken.Encoding() + decoder.schemaIdType = schemaIdEncoding.PrimitiveType() + decoder.schemaIdByteOrder = schemaIdEncoding.ByteOrder() + + if versionToken == nil { + return nil, fmt.Errorf("version token not found") + } + + decoder.schemaVersionOffset = versionToken.Offset() + versionEncoding := versionToken.Encoding() + decoder.schemaVersionType = versionEncoding.PrimitiveType() + decoder.schemaVersionByteOrder = versionEncoding.ByteOrder() + + return decoder, nil +} + +func (o *OftHeaderDecoder) EncodedLength() int32 { + return o.encodedLength +} + +// All elements must be unsigned integers according to Specification + +func (o *OftHeaderDecoder) TemplateId(headerBuffer []byte) (uint64, error) { + return GetAsUInt(o.templateIdType, o.templateIdByteOrder, headerBuffer[o.templateIdOffset:]) +} + +func (o *OftHeaderDecoder) SchemaId(headerBuffer []byte) (uint64, error) { + return GetAsUInt(o.schemaIdType, o.schemaIdByteOrder, headerBuffer[o.schemaIdOffset:]) +} + +func (o *OftHeaderDecoder) SchemaVersion(headerBuffer []byte) (uint64, error) { + return GetAsUInt(o.schemaVersionType, o.schemaVersionByteOrder, headerBuffer[o.schemaVersionOffset:]) +} + +func (o *OftHeaderDecoder) BlockLength(headerBuffer []byte) (uint64, error) { + return GetAsUInt(o.blockLengthType, o.blockLengthByteOrder, headerBuffer[o.blockLengthOffset:]) +} diff --git a/sbe-tool/src/main/golang/otf/otfmessagedecoder.go b/sbe-tool/src/main/golang/otf/otfmessagedecoder.go new file mode 100644 index 0000000000..09e070e120 --- /dev/null +++ b/sbe-tool/src/main/golang/otf/otfmessagedecoder.go @@ -0,0 +1,311 @@ +package otf + +import "fmt" + +type OnBeginMessage func(token Token) +type OnEndMessage func(token Token) +type OnEncoding func(token Token, buffer []byte, actingVersion uint64) +type OnEnum func(token Token, buffer []byte, tokens []Token, fromIndex int, toIndex int, actingVersion uint64) +type OnBitSet func(token Token, buffer []byte, tokens []Token, fromIndex int, toIndex int, actingVersion uint64) +type OnBeginComposite func(token Token, tokens []Token, fromIndex int, toIndex int) +type OnEndComposite func(token Token, tokens []Token, fromIndex int, toIndex int) +type OnGroupHeader func(token Token, numInGroup uint64) +type OnBeginGroup func(token Token, groupIndex uint64, numInGroup uint64) +type OnEndGroup func(token Token, groupIndex uint64, numInGroup uint64) +type OnVarData func(token Token, buffer []byte, length uint64, typeToken Token) + +type TokenListener interface { + OnBeginMessage(token Token) + OnEndMessage(token Token) + OnEncoding(fieldToken Token, buffer []byte, typeToken Token, actingVersion uint64) + OnEnum(fieldToken Token, buffer []byte, tokens []Token, fromIndex int, toIndex int, actingVersion uint64) + OnBitSet(fieldToken Token, buffer []byte, tokens []Token, fromIndex int, toIndex int, actingVersion uint64) + OnBeginComposite(fieldToken Token, tokens []Token, fromIndex int, toIndex int) + OnEndComposite(fieldToken Token, tokens []Token, fromIndex int, toIndex int) + OnGroupHeader(token Token, numInGroup uint64) + OnBeginGroup(token Token, groupIndex uint64, numInGroup uint64) + OnEndGroup(token Token, groupIndex uint64, numInGroup uint64) + OnVarData(fieldToken Token, buffer []byte, length uint64, typeToken Token) +} + +func DecodeComposite( + fieldToken Token, + buffer []byte, + bufferIndex int, + length uint64, + tokens []Token, + tokenIndex int, + toIndex int, + actingVersion uint64, + listener TokenListener) int { + listener.OnBeginComposite(fieldToken, tokens, tokenIndex, toIndex) + for i := tokenIndex + 1; i < toIndex; { + token := tokens[i] + nextFieldIndex := i + int(token.ComponentTokenCount()) + offset := int(token.Offset()) + switch token.Signal() { + case SignalBeginComposite: + DecodeComposite( + fieldToken, + buffer, + bufferIndex+offset, + length, + tokens, + i, + nextFieldIndex-1, + actingVersion, + listener) + case SignalBeginEnum: + listener.OnEnum( + fieldToken, + buffer[bufferIndex+offset:], + tokens, + i, + nextFieldIndex-1, + actingVersion) + case SignalBeginSet: + listener.OnBitSet( + fieldToken, + buffer[bufferIndex+offset:], + tokens, + i, + nextFieldIndex-1, + actingVersion) + case SignalEncoding: + listener.OnEncoding(token, buffer[bufferIndex+offset:], token, actingVersion) + } + i += int(token.ComponentTokenCount()) + } + listener.OnEndComposite(fieldToken, tokens, tokenIndex, toIndex) + return tokenIndex +} + +func DecodeFields( + buffer []byte, + bufferIndex int, + length uint64, + actingVersion uint64, + tokens []Token, + tokenIndex int, + numTokens int, + listener TokenListener) int { + + for tokenIndex < numTokens { + fieldToken := tokens[tokenIndex] + if SignalBeginField != fieldToken.Signal() { + break + } + nextFieldIndex := tokenIndex + int(fieldToken.ComponentTokenCount()) + tokenIndex++ + + typeToken := tokens[tokenIndex] + offset := bufferIndex + int(typeToken.Offset()) + switch typeToken.Signal() { + case SignalBeginComposite: + DecodeComposite( + fieldToken, + buffer, + offset, + length, + tokens, + tokenIndex, + nextFieldIndex-2, + actingVersion, + listener) + case SignalBeginEnum: + listener.OnEnum( + fieldToken, + buffer[offset:], + tokens, + tokenIndex, + nextFieldIndex-2, + actingVersion) + case SignalBeginSet: + listener.OnBitSet( + fieldToken, + buffer[offset:], + tokens, + tokenIndex, + nextFieldIndex-2, + actingVersion) + case SignalEncoding: + listener.OnEncoding(fieldToken, buffer[offset:], typeToken, actingVersion) + } + tokenIndex = nextFieldIndex + } + + return tokenIndex +} + +func DecodeData( + buffer []byte, + bufferIndex int, + length uint64, + tokens []Token, + tokenIndex int, + numTokens int, + actingVersion uint64, + listener TokenListener) int { + for tokenIndex < numTokens { + token := tokens[tokenIndex] + if SignalBeginVarData != token.Signal() { + break + } + isPresent := token.TokenVersion() <= int32(actingVersion) + lengthToken := tokens[tokenIndex+2] + dataToken := tokens[tokenIndex+3] + if (uint64(bufferIndex) + uint64(dataToken.Offset())) > length { + panic("length too short for data length field") + } + dataLength := uint64(0) + if isPresent { + lengthTokenEncoding := lengthToken.Encoding() + + var err error + dataLength, err = lengthTokenEncoding.GetAsUInt(buffer[bufferIndex+int(lengthToken.Offset()):]) + if err != nil { + panic(fmt.Errorf("invalid length encoding %s", err)) + } + bufferIndex += int(dataToken.Offset()) + } + + if (uint64(bufferIndex) + dataLength) > length { + panic("length too short for data field") + } + listener.OnVarData(token, buffer[bufferIndex:], dataLength, dataToken) + + bufferIndex += int(dataLength) + tokenIndex += int(token.ComponentTokenCount()) + } + return bufferIndex +} + +func DecodeGroups( + buffer []byte, + bufferIndex int, + length uint64, + actingVersion uint64, + tokens []Token, + tokenIndex int, + numTokens int, + listener TokenListener) (int, int) { + for tokenIndex < numTokens { + token := tokens[tokenIndex] + if SignalBeginGroup != token.Signal() { + break + } + isPresent := token.TokenVersion() <= int32(actingVersion) + dimensionsTypeComposite := tokens[tokenIndex+1] + dimensionsLength := int(dimensionsTypeComposite.EncodedLength()) + if (uint64(bufferIndex) + uint64(dimensionsLength)) > length { + panic("length too short for dimensions composite") + } + + blockLengthToken := tokens[tokenIndex+2] + numInGroupToken := tokens[tokenIndex+3] + blockLength := uint64(0) + numInGroup := uint64(0) + if isPresent { + blockLengthTokenEncoding := blockLengthToken.Encoding() + var err error + blockLength, err = blockLengthTokenEncoding.GetAsUInt(buffer[bufferIndex+int(blockLengthToken.Offset()):]) + if err != nil { + panic(fmt.Errorf("invalid block length encoding %s", err)) + } + numInGroupTokenEncoding := numInGroupToken.Encoding() + numInGroup, err = numInGroupTokenEncoding.GetAsUInt(buffer[bufferIndex+int(numInGroupToken.Offset()):]) + if err != nil { + panic(fmt.Errorf("invalid num in group encoding %s", err)) + } + bufferIndex += dimensionsLength + } + + beginFieldsIndex := tokenIndex + int(dimensionsTypeComposite.ComponentTokenCount()) + 1 + listener.OnGroupHeader(token, numInGroup) + for i := uint64(0); i < numInGroup; i++ { + listener.OnBeginGroup(token, i, numInGroup) + if (uint64(bufferIndex) + blockLength) > length { + panic("length too short for group blockLength") + } + afterFieldsIndex := DecodeFields( + buffer, + bufferIndex, + length, + actingVersion, + tokens, + beginFieldsIndex, + numTokens, + listener) + bufferIndex += int(blockLength) + groupIndex, groupNumTokens := DecodeGroups( + buffer, + bufferIndex, + length, + actingVersion, + tokens, + afterFieldsIndex, + numTokens, + listener) + bufferIndex = DecodeData( + buffer, + groupIndex, + length, + tokens, + groupNumTokens, + numTokens, + actingVersion, + listener, + ) + listener.OnEndGroup(token, i, numInGroup) + } + tokenIndex += int(token.ComponentTokenCount()) + } + return bufferIndex, tokenIndex +} + +func Decode( + buffer []byte, + actingVersion uint64, + blockLength uint64, + tokens []Token, + listener TokenListener) int { + listener.OnBeginMessage(tokens[0]) + length := uint64(len(buffer)) + if length < blockLength { + panic("length too short for message blockLength") + } + numTokens := len(tokens) + tokenIndex := DecodeFields( + buffer, + 0, + length, + actingVersion, + tokens, + 1, + numTokens, + listener, + ) + bufferIndex := int(blockLength) + groupIndex, groupNumTokens := DecodeGroups( + buffer, + bufferIndex, + length, + actingVersion, + tokens, + tokenIndex, + numTokens, + listener, + ) + bufferIndex = DecodeData( + buffer, + groupIndex, + length, + tokens, + groupNumTokens, + numTokens, + actingVersion, + listener, + ) + listener.OnEndMessage(tokens[numTokens-1]) + return bufferIndex +} diff --git a/sbe-tool/src/main/golang/otf/token.go b/sbe-tool/src/main/golang/otf/token.go new file mode 100644 index 0000000000..8b781fb7e3 --- /dev/null +++ b/sbe-tool/src/main/golang/otf/token.go @@ -0,0 +1,149 @@ +package otf + +import ( + "fmt" +) + +type Signal int + +const ( + SignalBeginMessage Signal = 1 + SignalEndMessage Signal = 2 + SignalBeginComposite Signal = 3 + SignalEndComposite Signal = 4 + SignalBeginField Signal = 5 + SignalEndField Signal = 6 + SignalBeginGroup Signal = 7 + SignalEndGroup Signal = 8 + SignalBeginEnum Signal = 9 + SignalValidValue Signal = 10 + SignalEndEnum Signal = 11 + SignalBeginSet Signal = 12 + SignalChoice Signal = 13 + SignalEndSet Signal = 14 + SignalBeginVarData Signal = 15 + SignalEndVarData Signal = 16 + SignalEncoding Signal = 17 +) + +func (s Signal) String() string { + switch s { + case SignalBeginMessage: + return "BeginMessage" + case SignalEndMessage: + return "EndMessage" + case SignalBeginComposite: + return "BeginComposite" + case SignalEndComposite: + return "EndComposite" + case SignalBeginField: + return "BeginField" + case SignalEndField: + return "EndField" + case SignalBeginGroup: + return "BeginGroup" + case SignalEndGroup: + return "EndGroup" + case SignalBeginEnum: + return "BeginEnum" + case SignalValidValue: + return "ValidValue" + case SignalEndEnum: + return "EndEnum" + case SignalBeginSet: + return "BeginSet" + case SignalChoice: + return "Choice" + case SignalEndSet: + return "EndSet" + case SignalBeginVarData: + return "BeginVarData" + case SignalEndVarData: + return "EndVarData" + case SignalEncoding: + return "Encoding" + default: + return "Unknown" + } +} + +type Token struct { + offset int32 + fieldId int32 + version int32 + encodedLength int32 + componentTokenCount int32 + signal Signal + name string + description string + encoding Encoding +} + +func (token Token) Signal() Signal { + return token.signal +} + +func (token Token) Name() string { + return token.name +} + +func (token Token) Description() string { + return token.description +} + +func (token Token) FieldId() int32 { + return token.fieldId +} + +func (token Token) TokenVersion() int32 { + return token.version +} + +func (token Token) Encoding() Encoding { + return token.encoding +} + +// EncodedLength returns the length of the encoded primitive in bytes. +func (token Token) EncodedLength() int32 { + return token.encodedLength +} + +// ArrayLength returns the number of encoded primitives in this type. +func (token Token) ArrayLength() int32 { + if token.encoding.PrimitiveType() == NONE || token.encodedLength == 0 { + return 0 + } + return token.encodedLength / int32(token.encoding.PrimitiveType().Size()) +} + +func (token Token) Offset() int32 { + return token.offset +} + +func (token Token) ComponentTokenCount() int32 { + return token.componentTokenCount +} + +// IsConstantEncoding returns true if the encoding presence is constant or false if not. +func (token Token) IsConstantEncoding() bool { + return token.encoding.presence == SbeConstant +} + +// IsOptionalEncoding returns true if the encoding presence is optional or false if not. +func (token Token) IsOptionalEncoding() bool { + return token.encoding.presence == SbeOptional +} + +func (token Token) String() string { + return fmt.Sprintf( + "Token{signal: %s, name: %s, description: %s, fieldId: %d, tokenVersion: %d, encoding: %s, encodedLength: %d, offset: %d, componentTokenCount: %d}", + token.signal, + token.name, + token.description, + token.fieldId, + token.version, + &token.encoding, + token.encodedLength, + token.offset, + token.componentTokenCount) +} From 870540ef9a2c33535172b2b946c43d0f2fc660a4 Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Fri, 25 Aug 2023 00:24:55 -0400 Subject: [PATCH 11/21] [Go] Add OTF Json Printer --- gocode/src/flyweight/otf/json_test.go | 114 +++++ gocode/src/flyweight/otf/otf_test.go | 306 ------------- gocode/src/flyweight/otf/testlistener.go | 311 +++++++++++++ sbe-tool/src/main/golang/json/jsonprinter.go | 75 ++++ .../src/main/golang/json/jsontokenlistener.go | 409 ++++++++++++++++++ sbe-tool/src/main/golang/otf/encoding.go | 43 ++ sbe-tool/src/main/golang/otf/irdecoder.go | 4 + 7 files changed, 956 insertions(+), 306 deletions(-) create mode 100644 gocode/src/flyweight/otf/json_test.go create mode 100644 gocode/src/flyweight/otf/testlistener.go create mode 100644 sbe-tool/src/main/golang/json/jsonprinter.go create mode 100644 sbe-tool/src/main/golang/json/jsontokenlistener.go diff --git a/gocode/src/flyweight/otf/json_test.go b/gocode/src/flyweight/otf/json_test.go new file mode 100644 index 0000000000..7f694d3fbd --- /dev/null +++ b/gocode/src/flyweight/otf/json_test.go @@ -0,0 +1,114 @@ +package otf + +import ( + "github.com/real-logic/simple-binary-encoding/json" + "github.com/real-logic/simple-binary-encoding/otf" + "github.com/real-logic/simple-binary-encoding/otf/test/code_generation_test" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestJson(t *testing.T) { + assert := assert.New(t) + + ir := otf.NewIrDecoder() + if ir.DecodeFile(SchemaFilename) < 0 { + t.Errorf("Error decoding %s", SchemaFilename) + t.Fail() + } + printer, err := json.NewJsonPrinter(ir) + assert.NoErrorf(err, "Error creating JsonPrinter: %v", err) + assert.NotNil(printer) + + var car code_generation_test.Car + buffer := make([]byte, MsgBufferCapacity) + encodeCar(&car, buffer) + + headerDecoder, _ := otf.NewOtfHeaderDecoder(ir.Header()) + bufferOffset := 0 + schemaId, _ := headerDecoder.SchemaId(buffer[bufferOffset:]) + + if uint16(schemaId) != car.SbeSchemaId() { + t.Errorf("Invalid schema id: %d", schemaId) + } + + str, err := printer.PrintJson(buffer) + assert.NoErrorf(err, "Error printing JSON: %v", err) + expected := `{ + "serialNumber": 1234, + "modelYear": 2013, + "available": "T", + "code": "A", + "someNumbers": [0, 1, 2, 3, 4], + "vehicleCode": ["a", "b", "c", "d", "e", "f"], + "extras": { "sunRoof": false, "sportsPack": false, "cruiseControl": false }, + "discountedModel": "C", + "Engine": + { + "capacity": 2000, + "numCylinders": 4, + "maxRpm": "9000", + "manufacturerCode": ["1", "2", "3"], + "fuel": "Petrol", + "booster": + { + "BoostType": "NITROUS", + "horsePower": 200 + } + }, + "fuelFigures": [ + { + "speed": 30, + "mpg": 35.900001525878906, + "usageDescription": "Urban Cycle" + }, + { + "speed": 55, + "mpg": 49, + "usageDescription": "Combined Cycle" + }, + { + "speed": 75, + "mpg": 40, + "usageDescription": "Highway Cycle" + }], + "performanceFigures": [ + { + "octaneRating": 95, + "acceleration": [ + { + "mph": 30, + "seconds": 4 + }, + { + "mph": 60, + "seconds": 7.5 + }, + { + "mph": 100, + "seconds": 12.199999809265137 + }] + }, + { + "octaneRating": 99, + "acceleration": [ + { + "mph": 30, + "seconds": 3.799999952316284 + }, + { + "mph": 60, + "seconds": 7.099999904632568 + }, + { + "mph": 100, + "seconds": 11.800000190734863 + }] + }], + "manufacturer": "Honda", + "model": "Civic VTi", + "activationCode": "abcdef", + "color": "" +}` + assert.EqualValues(expected, str, "Message printer") +} diff --git a/gocode/src/flyweight/otf/otf_test.go b/gocode/src/flyweight/otf/otf_test.go index a35f819e22..87d0e3ed71 100644 --- a/gocode/src/flyweight/otf/otf_test.go +++ b/gocode/src/flyweight/otf/otf_test.go @@ -1,12 +1,9 @@ package otf import ( - "fmt" "github.com/real-logic/simple-binary-encoding/otf" "github.com/real-logic/simple-binary-encoding/otf/test/code_generation_test" "github.com/stretchr/testify/assert" - "strconv" - "strings" "testing" ) @@ -15,309 +12,6 @@ import ( const SchemaFilename = "code-generation-schema.sbeir" const MsgBufferCapacity = 4 * 1024 -type TestListener struct { - out strings.Builder - compositeLevel int - namedScope []string -} - -func (t *TestListener) OnBeginMessage(token otf.Token) { - t.namedScope = append(t.namedScope, token.Name()+".") -} - -func (t *TestListener) OnEndMessage(token otf.Token) { - t.namedScope = t.namedScope[:len(t.namedScope)-1] -} - -func (t *TestListener) OnEncoding(fieldToken otf.Token, buffer []byte, typeToken otf.Token, actingVersion uint64) { - value := t.readEncodingAsString(buffer, fieldToken, typeToken, uint64(fieldToken.TokenVersion()), actingVersion) - - t.printScope() - if t.compositeLevel > 0 { - t.out.WriteString(typeToken.Name()) - } else { - t.out.WriteString(fieldToken.Name()) - } - t.out.WriteString("=") - t.out.WriteString(value) - t.out.WriteString("\n") - -} - -func (t *TestListener) printScope() { - for i := range t.namedScope { - t.out.WriteString(t.namedScope[i]) - } -} - -func (t *TestListener) determineName( - thresholdLevel int, - fieldToken otf.Token, - tokens []otf.Token, - fromIndex int, -) string { - if t.compositeLevel > thresholdLevel { - return tokens[fromIndex].Name() - } else { - return fieldToken.Name() - } -} - -func (t *TestListener) readEncodingAsString( - buffer []byte, - fieldToken otf.Token, - typeToken otf.Token, - fieldVersion uint64, - actingVersion uint64, -) string { - constOrNotPresentValue := t.constOrNotPresentValue(typeToken, fieldVersion, actingVersion) - if constOrNotPresentValue.PrimitiveType != otf.NONE { - encoding := typeToken.Encoding() - characterEncoding := encoding.CharacterEncoding() - if constOrNotPresentValue.Size() == 1 && characterEncoding == "" { - // TODO java uses this - // final byte[] bytes = { (byte)constOrNotPresentValue.longValue() }; - // return new String(bytes, characterEncoding); - return constOrNotPresentValue.String() - } else { - value := constOrNotPresentValue.String() - size := typeToken.ArrayLength() - if size < 2 { - return value - } - sb := strings.Builder{} - more := false - for i := int32(0); i < size; i++ { - if more { - sb.WriteString(", ") - more = true - } - sb.WriteString(value) - } - return sb.String() - } - } - - sb := strings.Builder{} - encoding := typeToken.Encoding() - elementSize := int32(encoding.PrimitiveType().Size()) - - more := false - for i := int32(0); i < typeToken.ArrayLength(); i++ { - if more { - sb.WriteString(", ") - more = true - } - arrayValue := otf.NewPrimitiveValue(encoding.PrimitiveType(), buffer[i*elementSize:(i+1)*elementSize]) - sb.WriteString(arrayValue.String()) - } - return sb.String() -} - -func (t *TestListener) readEncodingAsInt( - buffer []byte, - typeToken otf.Token, - fieldVersion uint64, - actingVersion uint64, -) int64 { - constOrNotPresentValue := t.constOrNotPresentValue(typeToken, fieldVersion, actingVersion) - if constOrNotPresentValue.PrimitiveType != otf.NONE { - return constOrNotPresentValue.AsInt() - } - typeTokenEncoding := typeToken.Encoding() - v, _ := otf.GetAsInt(typeTokenEncoding.PrimitiveType(), typeTokenEncoding.ByteOrder(), buffer) - return v -} - -func (t *TestListener) readEncodingAsUInt( - buffer []byte, - typeToken otf.Token, - fieldVersion uint64, - actingVersion uint64, -) uint64 { - constOrNotPresentValue := t.constOrNotPresentValue(typeToken, fieldVersion, actingVersion) - if constOrNotPresentValue.PrimitiveType != otf.NONE { - return constOrNotPresentValue.AsUInt() - } - typeTokenEncoding := typeToken.Encoding() - v, _ := otf.GetAsUInt(typeTokenEncoding.PrimitiveType(), typeTokenEncoding.ByteOrder(), buffer) - return v -} - -func (t *TestListener) constOrNotPresentValue( - typeToken otf.Token, - fieldVersion uint64, - actingVersion uint64, -) otf.PrimitiveValue { - typeTokenEncoding := typeToken.Encoding() - if typeToken.IsConstantEncoding() { - return typeTokenEncoding.ConstValue() - } else if typeToken.IsOptionalEncoding() && actingVersion < fieldVersion { - return typeTokenEncoding.ApplicableNullValue() - } else { - return otf.PrimitiveValue{} - } -} - -func (t *TestListener) OnEnum( - fieldToken otf.Token, - buffer []byte, - tokens []otf.Token, - fromIndex int, - toIndex int, - actingVersion uint64, -) { - typeToken := tokens[fromIndex+1] - encoding := typeToken.Encoding() - - value := "" - if fieldToken.IsConstantEncoding() { - encoding := fieldToken.Encoding() - refValue := encoding.ConstValue() - indexOfDot := strings.LastIndex(refValue.String(), ".") - if indexOfDot == -1 { - value = refValue.String() - } else { - value = refValue.String()[indexOfDot+1:] - } - } else if encoding.PrimitiveType().IsUnsigned() { - encodedValue := t.readEncodingAsUInt( - buffer, - typeToken, - uint64(fieldToken.TokenVersion()), - actingVersion, - ) - for i := fromIndex + 1; i < toIndex; i++ { - encoding := tokens[i].Encoding() - constValue := encoding.ConstValue() - if constValue.AsUInt() == encodedValue { - value = tokens[i].Name() - break - } - } - } else { - encodedValue := t.readEncodingAsInt( - buffer, - typeToken, - uint64(fieldToken.TokenVersion()), - actingVersion, - ) - for i := fromIndex + 1; i < toIndex; i++ { - encoding := tokens[i].Encoding() - constValue := encoding.ConstValue() - if constValue.AsInt() == encodedValue { - value = tokens[i].Name() - break - } - } - } - - t.printScope() - t.out.WriteString(t.determineName(0, fieldToken, tokens, fromIndex)) - t.out.WriteString("=") - t.out.WriteString(value) - t.out.WriteString("\n") -} - -func (t *TestListener) OnBitSet( - fieldToken otf.Token, - buffer []byte, - tokens []otf.Token, - fromIndex int, - toIndex int, - actingVersion uint64, -) { - typeToken := tokens[fromIndex+1] - encodedValue := t.readEncodingAsInt( - buffer, - typeToken, - uint64(fieldToken.TokenVersion()), - actingVersion, - ) - t.printScope() - t.out.WriteString(t.determineName(0, fieldToken, tokens, fromIndex)) - t.out.WriteString(":") - for i := fromIndex + 1; i < toIndex; i++ { - t.out.WriteString(" ") - t.out.WriteString(tokens[i].Name()) - t.out.WriteString("=") - encoding := tokens[i].Encoding() - constValue := encoding.ConstValue() - bitPosition := constValue.AsInt() - flag := (encodedValue & (1 << uint64(bitPosition))) != 0 - t.out.WriteString(strconv.FormatBool(flag)) - } - t.out.WriteString("\n") -} - -func (t *TestListener) OnBeginComposite( - fieldToken otf.Token, - tokens []otf.Token, - fromIndex int, - toIndex int, -) { - t.compositeLevel++ - t.namedScope = append(t.namedScope, t.determineName(1, fieldToken, tokens, fromIndex)+".") -} - -func (t *TestListener) OnEndComposite( - token otf.Token, - tokens []otf.Token, - fromIndex int, - toIndex int, -) { - t.compositeLevel-- - t.namedScope = t.namedScope[:len(t.namedScope)-1] -} - -func (t *TestListener) OnGroupHeader( - token otf.Token, - numInGroup uint64, -) { - t.printScope() - t.out.WriteString(token.Name()) - t.out.WriteString(" Group Header : numInGroup=") - t.out.WriteString(strconv.FormatUint(numInGroup, 10)) - t.out.WriteString("\n") -} - -func (t *TestListener) OnBeginGroup( - token otf.Token, - groupIndex uint64, - numInGroup uint64, -) { - t.namedScope = append(t.namedScope, token.Name()+".") -} - -func (t *TestListener) OnEndGroup( - token otf.Token, - groupIndex uint64, - numInGroup uint64, -) { - t.namedScope = t.namedScope[:len(t.namedScope)-1] -} - -func (t *TestListener) OnVarData( - fieldToken otf.Token, - buffer []byte, - length uint64, - typeToken otf.Token, -) { - value := "" - typeTokenEncoding := typeToken.Encoding() - if characterEncoding := typeTokenEncoding.CharacterEncoding(); characterEncoding == "" { - value = fmt.Sprintf("%d bytes of raw data", length) - } else { - value = string(buffer[:length]) - } - - t.printScope() - t.out.WriteString(fieldToken.Name()) - t.out.WriteString("=") - t.out.WriteString(value) - t.out.WriteString("\n") -} - func TestOtf(t *testing.T) { assert := assert.New(t) diff --git a/gocode/src/flyweight/otf/testlistener.go b/gocode/src/flyweight/otf/testlistener.go new file mode 100644 index 0000000000..18bc73724f --- /dev/null +++ b/gocode/src/flyweight/otf/testlistener.go @@ -0,0 +1,311 @@ +package otf + +import ( + "fmt" + "github.com/real-logic/simple-binary-encoding/otf" + "strconv" + "strings" +) + +type TestListener struct { + out strings.Builder + compositeLevel int + namedScope []string +} + +func (t *TestListener) OnBeginMessage(token otf.Token) { + t.namedScope = append(t.namedScope, token.Name()+".") +} + +func (t *TestListener) OnEndMessage(token otf.Token) { + t.namedScope = t.namedScope[:len(t.namedScope)-1] +} + +func (t *TestListener) OnEncoding(fieldToken otf.Token, buffer []byte, typeToken otf.Token, actingVersion uint64) { + value := t.readEncodingAsString(buffer, fieldToken, typeToken, uint64(fieldToken.TokenVersion()), actingVersion) + + t.printScope() + if t.compositeLevel > 0 { + t.out.WriteString(typeToken.Name()) + } else { + t.out.WriteString(fieldToken.Name()) + } + t.out.WriteString("=") + t.out.WriteString(value) + t.out.WriteString("\n") + +} + +func (t *TestListener) printScope() { + for i := range t.namedScope { + t.out.WriteString(t.namedScope[i]) + } +} + +func (t *TestListener) determineName( + thresholdLevel int, + fieldToken otf.Token, + tokens []otf.Token, + fromIndex int, +) string { + if t.compositeLevel > thresholdLevel { + return tokens[fromIndex].Name() + } else { + return fieldToken.Name() + } +} + +func (t *TestListener) readEncodingAsString( + buffer []byte, + fieldToken otf.Token, + typeToken otf.Token, + fieldVersion uint64, + actingVersion uint64, +) string { + constOrNotPresentValue := t.constOrNotPresentValue(typeToken, fieldVersion, actingVersion) + if constOrNotPresentValue.PrimitiveType != otf.NONE { + encoding := typeToken.Encoding() + characterEncoding := encoding.CharacterEncoding() + if constOrNotPresentValue.Size() == 1 && characterEncoding == "" { + // TODO java uses this + // final byte[] bytes = { (byte)constOrNotPresentValue.longValue() }; + // return new String(bytes, characterEncoding); + return constOrNotPresentValue.String() + } else { + value := constOrNotPresentValue.String() + size := typeToken.ArrayLength() + if size < 2 { + return value + } + sb := strings.Builder{} + more := false + for i := int32(0); i < size; i++ { + if more { + sb.WriteString(", ") + more = true + } + sb.WriteString(value) + } + return sb.String() + } + } + + sb := strings.Builder{} + encoding := typeToken.Encoding() + elementSize := int32(encoding.PrimitiveType().Size()) + + more := false + for i := int32(0); i < typeToken.ArrayLength(); i++ { + if more { + sb.WriteString(", ") + more = true + } + arrayValue := otf.NewPrimitiveValue(encoding.PrimitiveType(), buffer[i*elementSize:(i+1)*elementSize]) + sb.WriteString(arrayValue.String()) + } + return sb.String() +} + +func (t *TestListener) readEncodingAsInt( + buffer []byte, + typeToken otf.Token, + fieldVersion uint64, + actingVersion uint64, +) int64 { + constOrNotPresentValue := t.constOrNotPresentValue(typeToken, fieldVersion, actingVersion) + if constOrNotPresentValue.PrimitiveType != otf.NONE { + return constOrNotPresentValue.AsInt() + } + typeTokenEncoding := typeToken.Encoding() + v, _ := otf.GetAsInt(typeTokenEncoding.PrimitiveType(), typeTokenEncoding.ByteOrder(), buffer) + return v +} + +func (t *TestListener) readEncodingAsUInt( + buffer []byte, + typeToken otf.Token, + fieldVersion uint64, + actingVersion uint64, +) uint64 { + constOrNotPresentValue := t.constOrNotPresentValue(typeToken, fieldVersion, actingVersion) + if constOrNotPresentValue.PrimitiveType != otf.NONE { + return constOrNotPresentValue.AsUInt() + } + typeTokenEncoding := typeToken.Encoding() + v, _ := otf.GetAsUInt(typeTokenEncoding.PrimitiveType(), typeTokenEncoding.ByteOrder(), buffer) + return v +} + +func (t *TestListener) constOrNotPresentValue( + typeToken otf.Token, + fieldVersion uint64, + actingVersion uint64, +) otf.PrimitiveValue { + typeTokenEncoding := typeToken.Encoding() + if typeToken.IsConstantEncoding() { + return typeTokenEncoding.ConstValue() + } else if typeToken.IsOptionalEncoding() && actingVersion < fieldVersion { + return typeTokenEncoding.ApplicableNullValue() + } else { + return otf.PrimitiveValue{} + } +} + +func (t *TestListener) OnEnum( + fieldToken otf.Token, + buffer []byte, + tokens []otf.Token, + fromIndex int, + toIndex int, + actingVersion uint64, +) { + typeToken := tokens[fromIndex+1] + encoding := typeToken.Encoding() + + value := "" + if fieldToken.IsConstantEncoding() { + encoding := fieldToken.Encoding() + refValue := encoding.ConstValue() + indexOfDot := strings.LastIndex(refValue.String(), ".") + if indexOfDot == -1 { + value = refValue.String() + } else { + value = refValue.String()[indexOfDot+1:] + } + } else if encoding.PrimitiveType().IsUnsigned() { + encodedValue := t.readEncodingAsUInt( + buffer, + typeToken, + uint64(fieldToken.TokenVersion()), + actingVersion, + ) + for i := fromIndex + 1; i < toIndex; i++ { + encoding := tokens[i].Encoding() + constValue := encoding.ConstValue() + if constValue.AsUInt() == encodedValue { + value = tokens[i].Name() + break + } + } + } else { + encodedValue := t.readEncodingAsInt( + buffer, + typeToken, + uint64(fieldToken.TokenVersion()), + actingVersion, + ) + for i := fromIndex + 1; i < toIndex; i++ { + encoding := tokens[i].Encoding() + constValue := encoding.ConstValue() + if constValue.AsInt() == encodedValue { + value = tokens[i].Name() + break + } + } + } + + t.printScope() + t.out.WriteString(t.determineName(0, fieldToken, tokens, fromIndex)) + t.out.WriteString("=") + t.out.WriteString(value) + t.out.WriteString("\n") +} + +func (t *TestListener) OnBitSet( + fieldToken otf.Token, + buffer []byte, + tokens []otf.Token, + fromIndex int, + toIndex int, + actingVersion uint64, +) { + typeToken := tokens[fromIndex+1] + encodedValue := t.readEncodingAsInt( + buffer, + typeToken, + uint64(fieldToken.TokenVersion()), + actingVersion, + ) + t.printScope() + t.out.WriteString(t.determineName(0, fieldToken, tokens, fromIndex)) + t.out.WriteString(":") + for i := fromIndex + 1; i < toIndex; i++ { + t.out.WriteString(" ") + t.out.WriteString(tokens[i].Name()) + t.out.WriteString("=") + encoding := tokens[i].Encoding() + constValue := encoding.ConstValue() + bitPosition := constValue.AsInt() + flag := (encodedValue & (1 << uint64(bitPosition))) != 0 + t.out.WriteString(strconv.FormatBool(flag)) + } + t.out.WriteString("\n") +} + +func (t *TestListener) OnBeginComposite( + fieldToken otf.Token, + tokens []otf.Token, + fromIndex int, + toIndex int, +) { + t.compositeLevel++ + t.namedScope = append(t.namedScope, t.determineName(1, fieldToken, tokens, fromIndex)+".") +} + +func (t *TestListener) OnEndComposite( + token otf.Token, + tokens []otf.Token, + fromIndex int, + toIndex int, +) { + t.compositeLevel-- + t.namedScope = t.namedScope[:len(t.namedScope)-1] +} + +func (t *TestListener) OnGroupHeader( + token otf.Token, + numInGroup uint64, +) { + t.printScope() + t.out.WriteString(token.Name()) + t.out.WriteString(" Group Header : numInGroup=") + t.out.WriteString(strconv.FormatUint(numInGroup, 10)) + t.out.WriteString("\n") +} + +func (t *TestListener) OnBeginGroup( + token otf.Token, + groupIndex uint64, + numInGroup uint64, +) { + t.namedScope = append(t.namedScope, token.Name()+".") +} + +func (t *TestListener) OnEndGroup( + token otf.Token, + groupIndex uint64, + numInGroup uint64, +) { + t.namedScope = t.namedScope[:len(t.namedScope)-1] +} + +func (t *TestListener) OnVarData( + fieldToken otf.Token, + buffer []byte, + length uint64, + typeToken otf.Token, +) { + value := "" + typeTokenEncoding := typeToken.Encoding() + if characterEncoding := typeTokenEncoding.CharacterEncoding(); characterEncoding == "" { + value = fmt.Sprintf("%d bytes of raw data", length) + } else { + value = string(buffer[:length]) + } + + t.printScope() + t.out.WriteString(fieldToken.Name()) + t.out.WriteString("=") + t.out.WriteString(value) + t.out.WriteString("\n") +} diff --git a/sbe-tool/src/main/golang/json/jsonprinter.go b/sbe-tool/src/main/golang/json/jsonprinter.go new file mode 100644 index 0000000000..e451abd37f --- /dev/null +++ b/sbe-tool/src/main/golang/json/jsonprinter.go @@ -0,0 +1,75 @@ +package json + +import ( + "fmt" + "github.com/real-logic/simple-binary-encoding/otf" + "strings" +) + +// JsonPrinter pretty prints JSON based upon the given Ir. +type JsonPrinter struct { + headerDecoder *otf.OftHeaderDecoder + ir *otf.IrDecoder +} + +// NewJsonPrinter creates a new JsonPrinter for a given message Ir. +func NewJsonPrinter(ir *otf.IrDecoder) (*JsonPrinter, error) { + d, err := otf.NewOtfHeaderDecoder(ir.Header()) + return &JsonPrinter{ + headerDecoder: d, + ir: ir, + }, err +} + +// Print the encoded message to the output. +func (printer *JsonPrinter) Print(buffer []byte, output *strings.Builder) error { + blockLength, err := printer.headerDecoder.BlockLength(buffer) + if err != nil { + return fmt.Errorf("Error getting blockLength: %s", err) + } + templateId, err := printer.headerDecoder.TemplateId(buffer) + if err != nil { + return fmt.Errorf("Error getting templateId: %s", err) + } + schemaId, err := printer.headerDecoder.SchemaId(buffer) + if err != nil { + return fmt.Errorf("Error getting schemaId: %s", err) + } + actingVersion, err := printer.headerDecoder.SchemaVersion(buffer) + if err != nil { + return fmt.Errorf("Error getting schemaVersion: %s", err) + } + err = printer.validateId(schemaId) + if err != nil { + return err + } + + messageOffset := printer.headerDecoder.EncodedLength() + msgTokens := printer.ir.MessageByID(int32(templateId)) + + otf.Decode( + buffer[messageOffset:], + actingVersion, + blockLength, + msgTokens, + newJsonTokenListener(output), + ) + + return nil +} + +func (printer *JsonPrinter) validateId(schemaId uint64) error { + if schemaId != uint64(printer.ir.Id()) { + return fmt.Errorf("Required schema id %d but was %d", printer.ir.Id(), schemaId) + } + return nil +} + +// PrintJson the encoded message to a String. +func (printer *JsonPrinter) PrintJson(buffer []byte) (string, error) { + sb := strings.Builder{} + if err := printer.Print(buffer, &sb); err != nil { + return "", err + } + return sb.String(), nil +} diff --git a/sbe-tool/src/main/golang/json/jsontokenlistener.go b/sbe-tool/src/main/golang/json/jsontokenlistener.go new file mode 100644 index 0000000000..9d5947d4cc --- /dev/null +++ b/sbe-tool/src/main/golang/json/jsontokenlistener.go @@ -0,0 +1,409 @@ +package json + +import ( + "encoding/hex" + "github.com/real-logic/simple-binary-encoding/otf" + "strconv" + "strings" +) + +// JsonTokenListener dynamically decodes to convert them to JSON for output. +type JsonTokenListener struct { + output *strings.Builder + indentation int + compositeLevel int + more bool +} + +func newJsonTokenListener(output *strings.Builder) *JsonTokenListener { + return &JsonTokenListener{ + output: output, + } +} + +func (t *JsonTokenListener) printScope() { + for i := 0; i < t.compositeLevel; i++ { + t.output.WriteString(" ") + } +} + +func isLastGroup(groupIndex uint64, numInGroup uint64) bool { + return groupIndex == numInGroup-1 +} + +func (t *JsonTokenListener) startObject() { + t.indent() + t.output.WriteString("{\n") + t.indentation++ + t.more = false +} + +func (t *JsonTokenListener) endObject() { + t.output.WriteString("\n") + t.indentation-- + t.indent() + t.output.WriteString("}") + t.more = true +} + +func (t *JsonTokenListener) property(name string) { + t.indent() + t.doubleQuote() + t.output.WriteString(name) + t.output.WriteString("\": ") +} + +func (t *JsonTokenListener) indent() { + for i := 0; i < t.indentation; i++ { + t.output.WriteString(" ") + } +} + +func (t *JsonTokenListener) doubleQuote() { + t.output.WriteString("\"") +} + +func (t *JsonTokenListener) prev() { + if t.more { + t.output.WriteString(",\n") + } +} + +func (t *JsonTokenListener) next() { + t.more = true +} + +func (t *JsonTokenListener) escapePrintableChar(buffer []byte, index int, size int, elementSize int) { + for i := 0; i < size; i++ { + c := buffer[index+i*elementSize] + if c > 0 { + t.escape(rune(c)) + } else { + break + } + } +} + +func (t *JsonTokenListener) escapeString(str string) { + for _, c := range str { + t.escape(c) + } +} + +func (t *JsonTokenListener) escape(c rune) { + if c == '"' || c == '\\' || c == '\b' || c == '\f' || c == '\n' || c == '\r' || c == '\t' { + t.output.WriteString("\\") + } + + t.output.WriteRune(c) +} + +func (t *JsonTokenListener) determineName( + thresholdLevel int, + fieldToken otf.Token, + tokens []otf.Token, + fromIndex int, +) string { + if t.compositeLevel > thresholdLevel { + return tokens[fromIndex].Name() + } else { + return fieldToken.Name() + } + +} + +func (t *JsonTokenListener) OnBeginMessage(token otf.Token) { + t.startObject() +} + +func (t *JsonTokenListener) OnEndMessage(token otf.Token) { + t.endObject() +} + +func (t *JsonTokenListener) OnEncoding(fieldToken otf.Token, buffer []byte, typeToken otf.Token, actingVersion uint64) { + t.prev() + name := fieldToken.Name() + if t.compositeLevel > 0 { + name = fieldToken.Name() + } + t.property(name) + t.appendEncodingAsString(buffer, typeToken, uint64(fieldToken.TokenVersion()), actingVersion) + t.next() +} + +func (t *JsonTokenListener) appendEncodingAsString( + buffer []byte, + typeToken otf.Token, + fieldVersion uint64, + actingVersion uint64, +) { + arrayLength := typeToken.ArrayLength() + encoding := typeToken.Encoding() + constOrNotPresentValue := t.constOrNotPresentValue(typeToken, fieldVersion, actingVersion) + if constOrNotPresentValue.PrimitiveType != otf.NONE { + characterEncoding := encoding.CharacterEncoding() + if characterEncoding == "" { + t.doubleQuote() + t.escapeString(constOrNotPresentValue.String()) + t.doubleQuote() + } else { + if arrayLength < 2 { + t.doubleQuote() + t.escapeString(constOrNotPresentValue.String()) + t.doubleQuote() + } else { + t.output.WriteString("[") + for i := int32(0); i < arrayLength; i++ { + if i > 0 { + t.output.WriteString(", ") + } + t.doubleQuote() + t.escapeString(constOrNotPresentValue.String()) + t.doubleQuote() + } + t.output.WriteString("]") + } + } + } else { + elementSize := encoding.PrimitiveType().Size() + if arrayLength < 1 && encoding.PrimitiveType() == otf.CHAR { + t.doubleQuote() + t.escapePrintableChar(buffer, 0, int(arrayLength), elementSize) + } else { + encoding := typeToken.Encoding() + elementSize := int32(encoding.PrimitiveType().Size()) + if arrayLength == 1 { + otf.AppendPrimitiveValueJson(t.output, otf.NewPrimitiveValue(encoding.PrimitiveType(), buffer)) + } else { + t.output.WriteString("[") + for i := int32(0); i < typeToken.ArrayLength(); i++ { + if i > 0 { + t.output.WriteString(", ") + } + otf.AppendPrimitiveValueJson(t.output, otf.NewPrimitiveValue(encoding.PrimitiveType(), + buffer[i*elementSize:(i+1)*elementSize])) + } + t.output.WriteString("]") + } + } + } +} + +func (t *JsonTokenListener) readEncodingAsInt( + buffer []byte, + typeToken otf.Token, + fieldVersion uint64, + actingVersion uint64, +) int64 { + constOrNotPresentValue := t.constOrNotPresentValue(typeToken, fieldVersion, actingVersion) + if constOrNotPresentValue.PrimitiveType != otf.NONE { + return constOrNotPresentValue.AsInt() + } + typeTokenEncoding := typeToken.Encoding() + v, _ := otf.GetAsInt(typeTokenEncoding.PrimitiveType(), typeTokenEncoding.ByteOrder(), buffer) + return v +} + +func (t *JsonTokenListener) readEncodingAsUInt( + buffer []byte, + typeToken otf.Token, + fieldVersion uint64, + actingVersion uint64, +) uint64 { + constOrNotPresentValue := t.constOrNotPresentValue(typeToken, fieldVersion, actingVersion) + if constOrNotPresentValue.PrimitiveType != otf.NONE { + return constOrNotPresentValue.AsUInt() + } + typeTokenEncoding := typeToken.Encoding() + v, _ := otf.GetAsUInt(typeTokenEncoding.PrimitiveType(), typeTokenEncoding.ByteOrder(), buffer) + return v +} + +func (t *JsonTokenListener) constOrNotPresentValue( + typeToken otf.Token, + fieldVersion uint64, + actingVersion uint64, +) otf.PrimitiveValue { + typeTokenEncoding := typeToken.Encoding() + if typeToken.IsConstantEncoding() { + return typeTokenEncoding.ConstValue() + } else if typeToken.IsOptionalEncoding() && actingVersion < fieldVersion { + return typeTokenEncoding.ApplicableNullValue() + } else { + return otf.PrimitiveValue{} + } +} + +func (t *JsonTokenListener) OnEnum( + fieldToken otf.Token, + buffer []byte, + tokens []otf.Token, + fromIndex int, + toIndex int, + actingVersion uint64, +) { + t.prev() + typeToken := tokens[fromIndex+1] + encoding := typeToken.Encoding() + + value := "" + if fieldToken.IsConstantEncoding() { + encoding := fieldToken.Encoding() + refValue := encoding.ConstValue() + indexOfDot := strings.LastIndex(refValue.String(), ".") + if indexOfDot == -1 { + value = refValue.String() + } else { + value = refValue.String()[indexOfDot+1:] + } + } else if encoding.PrimitiveType().IsUnsigned() { + encodedValue := t.readEncodingAsUInt( + buffer, + typeToken, + uint64(fieldToken.TokenVersion()), + actingVersion, + ) + for i := fromIndex + 1; i < toIndex; i++ { + encoding := tokens[i].Encoding() + constValue := encoding.ConstValue() + if constValue.AsUInt() == encodedValue { + value = tokens[i].Name() + break + } + } + } else { + encodedValue := t.readEncodingAsInt( + buffer, + typeToken, + uint64(fieldToken.TokenVersion()), + actingVersion, + ) + for i := fromIndex + 1; i < toIndex; i++ { + encoding := tokens[i].Encoding() + constValue := encoding.ConstValue() + if constValue.AsInt() == encodedValue { + value = tokens[i].Name() + break + } + } + } + + t.property(t.determineName(0, fieldToken, tokens, fromIndex)) + t.doubleQuote() + t.output.WriteString(value) + t.doubleQuote() + t.next() +} + +func (t *JsonTokenListener) OnBitSet( + fieldToken otf.Token, + buffer []byte, + tokens []otf.Token, + fromIndex int, + toIndex int, + actingVersion uint64, +) { + t.prev() + typeToken := tokens[fromIndex+1] + encodedValue := t.readEncodingAsInt( + buffer, + typeToken, + uint64(fieldToken.TokenVersion()), + actingVersion, + ) + t.property(t.determineName(0, fieldToken, tokens, fromIndex)) + + t.output.WriteString("{ ") + for i := fromIndex + 1; i < toIndex; i++ { + t.output.WriteString(`"`) + t.output.WriteString(tokens[i].Name()) + t.output.WriteString(`": `) + encoding := tokens[i].Encoding() + constValue := encoding.ConstValue() + bitPosition := constValue.AsInt() + flag := (encodedValue & (1 << uint64(bitPosition))) != 0 + t.output.WriteString(strconv.FormatBool(flag)) + if i < toIndex-1 { + t.output.WriteString(", ") + } + } + t.output.WriteString(` }`) + t.next() +} + +func (t *JsonTokenListener) OnBeginComposite( + fieldToken otf.Token, + tokens []otf.Token, + fromIndex int, + toIndex int, +) { + t.prev() + t.compositeLevel++ + t.property(t.determineName(0, fieldToken, tokens, fromIndex)) + t.output.WriteString("\n") + t.startObject() +} + +func (t *JsonTokenListener) OnEndComposite( + token otf.Token, + tokens []otf.Token, + fromIndex int, + toIndex int, +) { + t.compositeLevel-- + t.endObject() +} + +func (t *JsonTokenListener) OnGroupHeader( + token otf.Token, + numInGroup uint64, +) { + t.prev() + t.property(token.Name()) + if numInGroup > 0 { + t.output.WriteString("[\n") + t.more = false + } else { + t.output.WriteString("[]") + } +} + +func (t *JsonTokenListener) OnBeginGroup( + token otf.Token, + groupIndex uint64, + numInGroup uint64, +) { + t.prev() + t.startObject() +} + +func (t *JsonTokenListener) OnEndGroup( + token otf.Token, + groupIndex uint64, + numInGroup uint64, +) { + t.endObject() + if isLastGroup(groupIndex, numInGroup) { + t.output.WriteString("]") + } +} + +func (t *JsonTokenListener) OnVarData( + fieldToken otf.Token, + buffer []byte, + length uint64, + typeToken otf.Token, +) { + t.prev() + t.property(fieldToken.Name()) + t.doubleQuote() + + typeTokenEncoding := typeToken.Encoding() + if characterEncoding := typeTokenEncoding.CharacterEncoding(); characterEncoding == "" { + t.escapeString(hex.EncodeToString(buffer[:length])) + } else { + t.escapeString(string(buffer[:length])) + } + t.doubleQuote() + t.next() +} diff --git a/sbe-tool/src/main/golang/otf/encoding.go b/sbe-tool/src/main/golang/otf/encoding.go index a74f64ffda..d08ea86a61 100644 --- a/sbe-tool/src/main/golang/otf/encoding.go +++ b/sbe-tool/src/main/golang/otf/encoding.go @@ -20,6 +20,7 @@ import ( "fmt" "math" "strconv" + "strings" ) type ByteOrder int @@ -163,6 +164,48 @@ func (p *PrimitiveValue) String() string { } } +func AppendPrimitiveValue(sb *strings.Builder, p PrimitiveValue) { + switch p.PrimitiveType { + case CHAR: + if p.size > 1 { + sb.WriteString(p.arrayValue) + return + } + sb.WriteByte(byte(p.asInt)) + case INT8, INT16, INT32, INT64: + sb.WriteString(strconv.FormatInt(p.asInt, 10)) + case UINT8, UINT16, UINT32, UINT64: + sb.WriteString(strconv.FormatUint(p.asUInt, 10)) + case FLOAT, DOUBLE: + sb.WriteString(strconv.FormatFloat(p.asDouble, 'f', -1, 64)) + } +} + +func AppendPrimitiveValueJson(sb *strings.Builder, p PrimitiveValue) { + switch p.PrimitiveType { + case CHAR: + if p.size > 1 { + sb.WriteString(p.arrayValue) + return + } + sb.WriteRune('"') + sb.WriteByte(byte(p.asInt)) + sb.WriteRune('"') + case INT8, INT16, INT32, INT64: + sb.WriteString(strconv.FormatInt(p.asInt, 10)) + case UINT8: + sb.WriteString(strconv.FormatUint(p.asUInt&0xFF, 10)) + case UINT16: + sb.WriteString(strconv.FormatUint(p.asUInt&0xFFFF, 10)) + case UINT32: + sb.WriteString(strconv.FormatUint(p.asUInt&0xFFFFFFFF, 10)) + case UINT64: + sb.WriteString(strconv.FormatUint(p.asUInt, 10)) + case FLOAT, DOUBLE: + sb.WriteString(strconv.FormatFloat(p.asDouble, 'f', -1, 64)) + } +} + func NewPrimitiveValue(t PrimitiveType, value []byte) PrimitiveValue { p := PrimitiveValue{PrimitiveType: t} diff --git a/sbe-tool/src/main/golang/otf/irdecoder.go b/sbe-tool/src/main/golang/otf/irdecoder.go index ffe58c7a11..ccf80b2c7d 100644 --- a/sbe-tool/src/main/golang/otf/irdecoder.go +++ b/sbe-tool/src/main/golang/otf/irdecoder.go @@ -209,6 +209,10 @@ func (decoder *IrDecoder) readMessage(offset uint64) uint64 { return size } +func (decoder *IrDecoder) Id() int32 { + return decoder.id +} + func readFileIntoBuffer(buffer []byte, filename string, length uint64) error { f, err := os.Open(filename) if err != nil { From 0977fe58dee2a2be6ab5dcb23e4709408dc50a1b Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Sun, 27 Aug 2023 21:26:17 -0400 Subject: [PATCH 12/21] [Go] Fix SbeBlockAndHeaderLength and add constants for composite sizes --- gocode/src/flyweight/composite/Composite_test.go | 8 ++++++++ .../golang/flyweight/GolangFlyweightGenerator.java | 13 ++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/gocode/src/flyweight/composite/Composite_test.go b/gocode/src/flyweight/composite/Composite_test.go index 11f5f8ed83..20e5c9e757 100644 --- a/gocode/src/flyweight/composite/Composite_test.go +++ b/gocode/src/flyweight/composite/Composite_test.go @@ -9,6 +9,14 @@ func TestEncodeDecode(t *testing.T) { var in Composite in.WrapForEncode(data[:], 0, uint64(len(data))) + if in.SbeBlockAndHeaderLength() != 50 { + t.Logf("Failed to encode, expected %d, got %d", + 50, + in.SbeBlockAndHeaderLength(), + ) + t.Fail() + } + in.Start(). SetName("start"). SetD(3.14). diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java index e85b092216..0130e0dcfc 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java @@ -2046,7 +2046,8 @@ private CharSequence generateFixedFlyweightCode( String sizeValue = String.format("%1$d", size); if (size == -1) { - sizeValue = "SbeNullValueUint64()"; + addInclude("math"); + sizeValue = "math.MaxUint64"; } return String.format( @@ -2057,6 +2058,12 @@ private CharSequence generateFixedFlyweightCode( "%7$s\n" + "}\n\n" + + " const (\n" + + " %1$sEncodedLength uint64 = %2$s\n" + + " %1$sSbeSchemaID %3$s = %4$s\n" + + " %1$sSbeSchemaVersion %5$s = %6$s\n" + + " )\n\n" + + " func (m *%1$s) Wrap(\n" + " buffer []byte,\n" + " offset uint64,\n" + @@ -2066,7 +2073,7 @@ private CharSequence generateFixedFlyweightCode( " m.bufferLength = bufferLength\n" + " m.offset = offset\n" + " m.actingVersion = actingVersion\n" + - " if !SbeNoBoundsCheck && (uint64(int(m.offset) + int(%2$s)) > m.bufferLength) {\n" + + " if !SbeNoBoundsCheck && ((m.offset + %2$s) > m.bufferLength) {\n" + " panic(\"buffer too short for flyweight [E107]\")\n" + " }\n" + " }\n\n" + @@ -2190,7 +2197,7 @@ private CharSequence generateMessageFlyweightCode( " }\n\n" + " func (m *%10$s) SbeBlockAndHeaderLength() uint64 {\n" + - " return m.EncodedLength() + uint64(m.SbeBlockLength())\n" + + " return MessageHeaderEncodedLength + uint64(m.SbeBlockLength())\n" + " }\n\n" + " func (m *%10$s) SbeTemplateId() %3$s {\n" + From 3493d50fbb5fe120637405b8b1b9ca6c5eeee53c Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Wed, 13 Sep 2023 21:36:23 -0400 Subject: [PATCH 13/21] [Go] Add IrDecoder for runtime types --- sbe-tool/src/main/golang/otf/irdecoder.go | 120 +++++++++++++++++++++- sbe-tool/src/main/golang/otf/token.go | 5 + 2 files changed, 123 insertions(+), 2 deletions(-) diff --git a/sbe-tool/src/main/golang/otf/irdecoder.go b/sbe-tool/src/main/golang/otf/irdecoder.go index ccf80b2c7d..794ec99cd5 100644 --- a/sbe-tool/src/main/golang/otf/irdecoder.go +++ b/sbe-tool/src/main/golang/otf/irdecoder.go @@ -3,6 +3,8 @@ package otf import ( ir "github.com/real-logic/simple-binary-encoding/uk_co_real_logic_sbe_ir_generated" "os" + "sort" + "strings" ) type IrDecoder struct { @@ -11,10 +13,13 @@ type IrDecoder struct { buffer []byte length uint64 id int32 + typesByName map[string][]Token } func NewIrDecoder() *IrDecoder { - return &IrDecoder{} + return &IrDecoder{ + typesByName: make(map[string][]Token), + } } func (decoder *IrDecoder) Decode(irBuffer []byte) int { @@ -75,6 +80,11 @@ func (decoder *IrDecoder) MessageByID(id int32) []Token { return nil } +// TypeByName gets the type representation for a given type name. +func (decoder *IrDecoder) TypeByName(name string) []Token { + return decoder.typesByName[name] +} + func (decoder *IrDecoder) decodeIr() int { var frame ir.FrameCodec offset := uint64(0) @@ -111,6 +121,8 @@ func (decoder *IrDecoder) decodeIr() int { offset += decoder.readMessage(offset) } + decoder.captureTypes(decoder.headerTokens, 0, len(decoder.headerTokens)-1) + return 0 } func (decoder *IrDecoder) decodeAndAddToken(tokens *[]Token, offset uint64) uint64 { @@ -150,7 +162,7 @@ func (decoder *IrDecoder) decodeAndAddToken(tokens *[]Token, offset uint64) uint timeUnit := tokenCodec.GetTimeUnitAsString() semanticType := tokenCodec.GetSemanticTypeAsString() description := tokenCodec.GetDescriptionAsString() - tokenCodec.GetReferencedNameAsString() + referencedName := tokenCodec.GetReferencedNameAsString() encoding := NewEncoding( primitiveType, @@ -174,6 +186,7 @@ func (decoder *IrDecoder) decodeAndAddToken(tokens *[]Token, offset uint64) uint componentTokenCount: componentTokenCount, signal: signal, name: name, + referencedName: referencedName, description: description, encoding: encoding, } @@ -205,6 +218,10 @@ func (decoder *IrDecoder) readMessage(offset uint64) uint64 { break } } + + decoder.captureTypes(tokensForMessage, 0, len(tokensForMessage)-1) + decoder.updateComponentTokenCounts(tokensForMessage) + decoder.messages = append(decoder.messages, tokensForMessage) return size } @@ -235,3 +252,102 @@ func readFileIntoBuffer(buffer []byte, filename string, length uint64) error { return nil } + +func (decoder *IrDecoder) captureTypes(tokens []Token, beginIndex, endIndex int) { + for i := beginIndex; i < endIndex; i++ { + token := tokens[i] + typeBeginIndex := i + switch token.Signal() { + case SignalBeginComposite: + i = decoder.captureType( + tokens, + i, + SignalEndComposite, + token.Name(), + token.ReferencedName(), + ) + decoder.captureTypes(tokens, typeBeginIndex+1, i-1) + case SignalBeginEnum: + i = decoder.captureType( + tokens, + i, + SignalEndEnum, + token.Name(), + token.ReferencedName(), + ) + case SignalBeginSet: + i = decoder.captureType( + tokens, + i, + SignalEndSet, + token.Name(), + token.ReferencedName(), + ) + default: + } + } +} + +func (decoder *IrDecoder) captureType( + tokens []Token, + index int, + endSignal Signal, + name, referencedName string, +) int { + var typeTokens []Token + i := index + token := tokens[i] + typeTokens = append(typeTokens, token) + for { + i++ + token = tokens[i] + typeTokens = append(typeTokens, token) + if token.Signal() == endSignal || name == token.Name() { + break + } + } + + decoder.updateComponentTokenCounts(typeTokens) + + typeName := referencedName + if typeName == "" { + typeName = name + } + decoder.typesByName[typeName] = typeTokens + return i +} + +// updateComponentTokenCounts iterates over a list of Tokens and updates +// their counts of how many tokens make up each component. +func (decoder *IrDecoder) updateComponentTokenCounts(tokens []Token) { + beginIndexMap := make(map[string][]int) + for i, size := 0, len(tokens); i < size; i++ { + token := tokens[i] + signal := token.Signal() + if strings.HasPrefix(signal.String(), "Begin") { + componentType := signal.String()[5:] + beginIndexMap[componentType] = append(beginIndexMap[componentType], i) + } else if strings.HasPrefix(signal.String(), "End") { + componentType := signal.String()[3:] + beginIndices := beginIndexMap[componentType] + beginIndex := beginIndices[0] + beginIndexMap[componentType] = beginIndices[1:] + componentTokenCount := int32((i - beginIndex) + 1) + tokens[beginIndex].componentTokenCount = componentTokenCount + token.componentTokenCount = componentTokenCount + } + } +} + +func (decoder *IrDecoder) Types() [][]Token { + var typeNames []string + for name := range decoder.typesByName { + typeNames = append(typeNames, name) + } + sort.Strings(typeNames) + var types [][]Token + for _, name := range typeNames { + types = append(types, decoder.typesByName[name]) + } + return types +} diff --git a/sbe-tool/src/main/golang/otf/token.go b/sbe-tool/src/main/golang/otf/token.go index 8b781fb7e3..1e812ce2db 100644 --- a/sbe-tool/src/main/golang/otf/token.go +++ b/sbe-tool/src/main/golang/otf/token.go @@ -75,10 +75,15 @@ type Token struct { componentTokenCount int32 signal Signal name string + referencedName string description string encoding Encoding } +func (token Token) ReferencedName() string { + return token.referencedName +} + func (token Token) Signal() Signal { return token.signal } From 7a0ae0d3596b603bbb31dd2cc303e79616848b68 Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Wed, 13 Sep 2023 22:08:23 -0400 Subject: [PATCH 14/21] [Go] Add enum get method --- .../flyweight/GolangFlyweightGenerator.java | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java index 0130e0dcfc..62bfa83065 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java @@ -1300,7 +1300,7 @@ private void generateEnum(final List tokens) throws IOException out.append(generateEnumValues(tokens.subList(1, tokens.size() - 1), enumName, enumToken)); -// out.append(generateEnumLookupMethod(tokens.subList(1, tokens.size() - 1), enumToken)); + out.append(generateEnumLookupMethod(tokens.subList(1, tokens.size() - 1), enumToken)); out.append(generateEnumDisplay(tokens.subList(1, tokens.size() - 1), enumToken)); @@ -1442,6 +1442,53 @@ private CharSequence generateEnumValues( return sb; } + + private CharSequence generateEnumLookupMethod(final List tokens, final Token encodingToken) + { + final String enumName = formatClassName(encodingToken.applicableTypeName()); + final StringBuilder sb = new StringBuilder(); + final String goTypeName = goTypeName(encodingToken.encoding().primitiveType()); + + addInclude("errors"); + new Formatter(sb).format( + " var %1$sLookupErr = errors.New(\"unknown value for enum %1$s [E103]\")\n" + + " func Lookup%1$s(value %2$s) (%1$s, error) {\n" + + " switch value {\n", + enumName, + goTypeName); + + for (final Token token : tokens) + { + final CharSequence constVal = generateLiteral( + token.encoding().primitiveType(), token.encoding().constValue().toString()); + + sb.append(" case ").append(constVal) + .append(": return ").append(enumName).append("_") + .append(token.name()).append(", nil\n"); + } + + final CharSequence nullVal = generateLiteral( + encodingToken.encoding().primitiveType(), encodingToken.encoding().applicableNullValue().toString()); + + sb.append(" case ").append(nullVal).append(": return ") + .append(enumName).append("_").append("NULL_VALUE, nil\n") + .append(" }\n\n"); + + if (shouldDecodeUnknownEnumValues) + { + + sb.append(" return ").append(enumName).append("_").append("UNKNOWN").append(", nil\n}\n"); + } + else + { + new Formatter(sb).format(" return 0, %1$sLookupErr\n}\n", + enumName); + } + + return sb; + } + + private CharSequence generateFieldNotPresentCondition( final int sinceVersion, final Encoding encoding, final String indent) { From 35bd72a7c3d13c4b239c586b84e08221de478de1 Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Fri, 3 Nov 2023 15:45:01 -0400 Subject: [PATCH 15/21] [Go] Fixes for updating component counts in irdecoder --- sbe-tool/src/main/golang/otf/irdecoder.go | 5 +++-- sbe-tool/src/main/golang/otf/token.go | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/sbe-tool/src/main/golang/otf/irdecoder.go b/sbe-tool/src/main/golang/otf/irdecoder.go index 794ec99cd5..58a052f999 100644 --- a/sbe-tool/src/main/golang/otf/irdecoder.go +++ b/sbe-tool/src/main/golang/otf/irdecoder.go @@ -330,8 +330,9 @@ func (decoder *IrDecoder) updateComponentTokenCounts(tokens []Token) { } else if strings.HasPrefix(signal.String(), "End") { componentType := signal.String()[3:] beginIndices := beginIndexMap[componentType] - beginIndex := beginIndices[0] - beginIndexMap[componentType] = beginIndices[1:] + last := len(beginIndices) - 1 + beginIndex := beginIndices[last] + beginIndexMap[componentType] = beginIndices[:last] componentTokenCount := int32((i - beginIndex) + 1) tokens[beginIndex].componentTokenCount = componentTokenCount token.componentTokenCount = componentTokenCount diff --git a/sbe-tool/src/main/golang/otf/token.go b/sbe-tool/src/main/golang/otf/token.go index 1e812ce2db..442b6d5d80 100644 --- a/sbe-tool/src/main/golang/otf/token.go +++ b/sbe-tool/src/main/golang/otf/token.go @@ -104,6 +104,14 @@ func (token Token) TokenVersion() int32 { return token.version } +// ApplicableTypeName returns the name of the type that should be applied in context. +func (token Token) ApplicableTypeName() string { + if token.referencedName == "" { + return token.name + } + return token.referencedName +} + func (token Token) Encoding() Encoding { return token.encoding } From 6778b0ef8143d5b2b2f7775275a889bce5bd29d7 Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Fri, 3 Nov 2023 15:57:28 -0400 Subject: [PATCH 16/21] [Go] Run goimports --- sbe-tool/src/main/golang/json/jsonprinter.go | 3 ++- sbe-tool/src/main/golang/json/jsontokenlistener.go | 3 ++- sbe-tool/src/main/golang/otf/irdecoder.go | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/sbe-tool/src/main/golang/json/jsonprinter.go b/sbe-tool/src/main/golang/json/jsonprinter.go index e451abd37f..d6ec189f7f 100644 --- a/sbe-tool/src/main/golang/json/jsonprinter.go +++ b/sbe-tool/src/main/golang/json/jsonprinter.go @@ -2,8 +2,9 @@ package json import ( "fmt" - "github.com/real-logic/simple-binary-encoding/otf" "strings" + + "github.com/real-logic/simple-binary-encoding/otf" ) // JsonPrinter pretty prints JSON based upon the given Ir. diff --git a/sbe-tool/src/main/golang/json/jsontokenlistener.go b/sbe-tool/src/main/golang/json/jsontokenlistener.go index 9d5947d4cc..af7c711f64 100644 --- a/sbe-tool/src/main/golang/json/jsontokenlistener.go +++ b/sbe-tool/src/main/golang/json/jsontokenlistener.go @@ -2,9 +2,10 @@ package json import ( "encoding/hex" - "github.com/real-logic/simple-binary-encoding/otf" "strconv" "strings" + + "github.com/real-logic/simple-binary-encoding/otf" ) // JsonTokenListener dynamically decodes to convert them to JSON for output. diff --git a/sbe-tool/src/main/golang/otf/irdecoder.go b/sbe-tool/src/main/golang/otf/irdecoder.go index 58a052f999..d90876e637 100644 --- a/sbe-tool/src/main/golang/otf/irdecoder.go +++ b/sbe-tool/src/main/golang/otf/irdecoder.go @@ -1,10 +1,11 @@ package otf import ( - ir "github.com/real-logic/simple-binary-encoding/uk_co_real_logic_sbe_ir_generated" "os" "sort" "strings" + + ir "github.com/real-logic/simple-binary-encoding/uk_co_real_logic_sbe_ir_generated" ) type IrDecoder struct { From 6a013abc25940646fdb553e07774bafba2614965 Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Tue, 17 Sep 2024 18:20:37 -0400 Subject: [PATCH 17/21] [Go] Update with latest changes --- .../generation/golang/flyweight/GolangFlyweightGenerator.java | 2 +- .../golang/flyweight/GolangFlyweightOutputManager.java | 2 +- .../sbe/generation/golang/flyweight/GolangFlyweightUtil.java | 2 +- ...GolangGeneratorTest.java => GolangStructGeneratorTest.java} | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) rename sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/golang/{GolangGeneratorTest.java => GolangStructGeneratorTest.java} (99%) diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java index 62bfa83065..de9d86f8cc 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 Real Logic Limited. + * Copyright 2013-2024 Real Logic Limited. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightOutputManager.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightOutputManager.java index 954803ca29..3b9e72b716 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightOutputManager.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightOutputManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 Real Logic Limited. + * Copyright 2013-2024 Real Logic Limited. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightUtil.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightUtil.java index 21509c1472..78ac285b6c 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightUtil.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/golang/flyweight/GolangFlyweightUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 Real Logic Limited. + * Copyright 2013-2024 Real Logic Limited. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/golang/GolangGeneratorTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/golang/GolangStructGeneratorTest.java similarity index 99% rename from sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/golang/GolangGeneratorTest.java rename to sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/golang/GolangStructGeneratorTest.java index af647727f2..b4d6198680 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/golang/GolangGeneratorTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/golang/GolangStructGeneratorTest.java @@ -18,6 +18,7 @@ import org.agrona.generation.StringWriterOutputManager; import org.junit.jupiter.api.Test; import uk.co.real_logic.sbe.Tests; +import uk.co.real_logic.sbe.generation.golang.struct.GolangGenerator; import uk.co.real_logic.sbe.ir.Ir; import uk.co.real_logic.sbe.xml.IrGenerator; import uk.co.real_logic.sbe.xml.MessageSchema; @@ -28,7 +29,7 @@ import static org.junit.jupiter.api.Assertions.*; import static uk.co.real_logic.sbe.xml.XmlSchemaParser.parse; -class GolangGeneratorTest +class GolangStructGeneratorTest { @Test @SuppressWarnings("MethodLength") From 67164ae29ef9925c9379b024a04a19c4623568ec Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Tue, 17 Sep 2024 20:04:04 -0400 Subject: [PATCH 18/21] [Go] Change the file structure for GOPATH --- .gitignore | 63 ++++--------------- build.gradle | 24 +++---- gocode/Makefile | 58 ++++++++--------- .../src}/baseline-bigendian/Car_test.go | 0 .../src}/baseline/Car_test.go | 0 .../src}/basic/SBE tests/ENUM_test.go | 0 .../src}/basic/SBE tests/Message1_test.go | 0 .../src}/basic/SBE tests/SET_test.go | 0 .../src}/composite/Composite_test.go | 0 .../src}/composite_elements/Message_test.go | 0 .../src}/example-schema/CarExample_test.go | 0 .../src}/group/SBE tests/TestMessage1_test.go | 0 .../src}/group_with_data/TestMessages_test.go | 0 .../Extension_test.go | 2 +- .../src}/issue435/Issue435_test.go | 0 .../src}/issue472/Issue472_test.go | 0 .../src}/issue483/Issue483_test.go | 0 .../src}/issue488/Issue488_test.go | 0 .../src}/issue560/Issue560_test.go | 0 .../src}/issue847/issue847_test.go | 0 .../src}/issue848/issue848_test.go | 0 .../src}/issue849/issue849_test.go | 0 .../src}/mktdata/fixme_test.go | 0 .../flyweight => flyweight/src}/otf/go.mod | 0 .../flyweight => flyweight/src}/otf/go.sum | 0 .../src}/otf/json_test.go | 0 .../src}/otf/otf_test.go | 0 .../src}/otf/testlistener.go | 0 .../src}/simple/simple_test.go | 0 .../since-deprecated/SinceDeprecated_test.go | 0 .../vardata/SBE tests/TestMessage1_test.go | 0 .../src}/baseline-bigendian/Car_test.go | 0 .../src}/baseline/Car_test.go | 0 .../src}/basic/SBE tests/ENUM_test.go | 0 .../src}/basic/SBE tests/Message1_test.go | 0 .../src}/basic/SBE tests/SET_test.go | 0 .../src}/composite/Composite_test.go | 0 .../src}/composite_elements/Message_test.go | 0 .../src}/example-schema/CarExample.go | 0 .../src}/example-schema/CarExample_test.go | 0 .../clientserver.go | 0 .../src}/group/SBE tests/TestMessage1_test.go | 0 .../src}/group_with_data/TestMessages_test.go | 0 .../Extension_test.go | 2 +- .../src}/issue435/Issue435_test.go | 0 .../src}/issue472/Issue472_test.go | 0 .../src}/issue483/Issue483_test.go | 0 .../src}/issue488/Issue488_test.go | 0 .../src/issue505/Issue505_test.go | 0 .../src}/issue560/Issue560_test.go | 0 .../src}/issue847/issue847_test.go | 0 .../src}/issue848/issue848_test.go | 0 .../src}/issue849/issue849_test.go | 0 .../src}/mktdata/fixme_test.go | 0 .../src}/mktdata/preallocated_test.go | 0 .../src}/simple/simple_test.go | 0 .../since-deprecated/SinceDeprecated_test.go | 0 gocode/{ => struct}/src/test973/EventType.go | 0 .../{ => struct}/src/test973/MessageHeader.go | 0 .../src/test973/SbeMarshalling.go | 0 .../{ => struct}/src/test973/SomeMessage.go | 0 .../{ => struct}/src/test973/test973_test.go | 0 .../vardata/SBE tests/TestMessage1_test.go | 0 63 files changed, 54 insertions(+), 95 deletions(-) rename gocode/{src/flyweight => flyweight/src}/baseline-bigendian/Car_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/baseline/Car_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/basic/SBE tests/ENUM_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/basic/SBE tests/Message1_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/basic/SBE tests/SET_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/composite/Composite_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/composite_elements/Message_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/example-schema/CarExample_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/group/SBE tests/TestMessage1_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/group_with_data/TestMessages_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/group_with_data_extension/Extension_test.go (99%) rename gocode/{src/flyweight => flyweight/src}/issue435/Issue435_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/issue472/Issue472_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/issue483/Issue483_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/issue488/Issue488_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/issue560/Issue560_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/issue847/issue847_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/issue848/issue848_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/issue849/issue849_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/mktdata/fixme_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/otf/go.mod (100%) rename gocode/{src/flyweight => flyweight/src}/otf/go.sum (100%) rename gocode/{src/flyweight => flyweight/src}/otf/json_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/otf/otf_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/otf/testlistener.go (100%) rename gocode/{src/flyweight => flyweight/src}/simple/simple_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/since-deprecated/SinceDeprecated_test.go (100%) rename gocode/{src/flyweight => flyweight/src}/vardata/SBE tests/TestMessage1_test.go (100%) rename gocode/{src/struct => struct/src}/baseline-bigendian/Car_test.go (100%) rename gocode/{src/struct => struct/src}/baseline/Car_test.go (100%) rename gocode/{src/struct => struct/src}/basic/SBE tests/ENUM_test.go (100%) rename gocode/{src/struct => struct/src}/basic/SBE tests/Message1_test.go (100%) rename gocode/{src/struct => struct/src}/basic/SBE tests/SET_test.go (100%) rename gocode/{src/struct => struct/src}/composite/Composite_test.go (100%) rename gocode/{src/struct => struct/src}/composite_elements/Message_test.go (100%) rename gocode/{src/struct => struct/src}/example-schema/CarExample.go (100%) rename gocode/{src/struct => struct/src}/example-schema/CarExample_test.go (100%) rename gocode/{src/struct => struct/src}/example-socket-clientserver/clientserver.go (100%) rename gocode/{src/struct => struct/src}/group/SBE tests/TestMessage1_test.go (100%) rename gocode/{src/struct => struct/src}/group_with_data/TestMessages_test.go (100%) rename gocode/{src/struct => struct/src}/group_with_data_extension/Extension_test.go (99%) rename gocode/{src/struct => struct/src}/issue435/Issue435_test.go (100%) rename gocode/{src/struct => struct/src}/issue472/Issue472_test.go (100%) rename gocode/{src/struct => struct/src}/issue483/Issue483_test.go (100%) rename gocode/{src/struct => struct/src}/issue488/Issue488_test.go (100%) rename gocode/{ => struct}/src/issue505/Issue505_test.go (100%) rename gocode/{src/struct => struct/src}/issue560/Issue560_test.go (100%) rename gocode/{src/struct => struct/src}/issue847/issue847_test.go (100%) rename gocode/{src/struct => struct/src}/issue848/issue848_test.go (100%) rename gocode/{src/struct => struct/src}/issue849/issue849_test.go (100%) rename gocode/{src/struct => struct/src}/mktdata/fixme_test.go (100%) rename gocode/{src/struct => struct/src}/mktdata/preallocated_test.go (100%) rename gocode/{src/struct => struct/src}/simple/simple_test.go (100%) rename gocode/{src/struct => struct/src}/since-deprecated/SinceDeprecated_test.go (100%) rename gocode/{ => struct}/src/test973/EventType.go (100%) rename gocode/{ => struct}/src/test973/MessageHeader.go (100%) rename gocode/{ => struct}/src/test973/SbeMarshalling.go (100%) rename gocode/{ => struct}/src/test973/SomeMessage.go (100%) rename gocode/{ => struct}/src/test973/test973_test.go (100%) rename gocode/{src/struct => struct/src}/vardata/SBE tests/TestMessage1_test.go (100%) diff --git a/.gitignore b/.gitignore index 834c7b6a7b..363f445f20 100644 --- a/.gitignore +++ b/.gitignore @@ -64,58 +64,17 @@ cppbuild/Release cppbuild/Win32 # golang -gocode/pkg -gocode/src/struct/baseline/*.go -!gocode/src/struct/baseline/*_test.go -gocode/src/struct/baseline-bigendian/*.go -!gocode/src/struct/baseline-bigendian/*_test.go -gocode/src/struct/composite/*.go -!gocode/src/struct/composite/*_test.go -gocode/src/struct/composite_elements/*.go -!gocode/src/struct/composite_elements/*_test.go -gocode/src/struct/since-deprecated/*.go -!gocode/src/struct/since-deprecated/*_test.go -gocode/src/struct/group_with_data*/*.go -!gocode/src/struct/group_with_data*/*_test.go -gocode/src/struct/mktdata/*.go -!gocode/src/struct/mktdata/*_test.go -gocode/src/struct/simple/*.go -!gocode/src/struct/simple/*_test.go -gocode/src/struct/issue*/*.go -!gocode/src/struct/issue*/*_test.go -gocode/src/struct/*/*/*.go -!gocode/src/struct/*/*/*_test.go -gocode/src/struct/example-schema/example-schema* -gocode/src/struct/example-schema/cpu.out -gocode/src/struct/example-socket-clientserver/example-socket-clientserver -gocode/src/struct/extension -gocode/src/struct/extension2 -gocode/src/flyweight/baseline/*.go -!gocode/src/flyweight/baseline/*_test.go -gocode/src/flyweight/baseline-bigendian/*.go -!gocode/src/flyweight/baseline-bigendian/*_test.go -gocode/src/flyweight/composite/*.go -!gocode/src/flyweight/composite/*_test.go -gocode/src/flyweight/composite_elements/*.go -!gocode/src/flyweight/composite_elements/*_test.go -gocode/src/flyweight/since-deprecated/*.go -!gocode/src/flyweight/since-deprecated/*_test.go -gocode/src/flyweight/group_with_data*/*.go -!gocode/src/flyweight/group_with_data*/*_test.go -gocode/src/flyweight/mktdata/*.go -!gocode/src/flyweight/mktdata/*_test.go -gocode/src/flyweight/simple/*.go -!gocode/src/flyweight/simple/*_test.go -gocode/src/flyweight/issue*/*.go -!gocode/src/flyweight/issue*/*_test.go -gocode/src/flyweight/*/*/*.go -!gocode/src/flyweight/*/*/*_test.go -gocode/src/flyweight/example-schema/example-schema* -gocode/src/flyweight/example-schema/cpu.out -gocode/src/flyweight/example-socket-clientserver/example-socket-clientserver -gocode/src/flyweight/extension -gocode/src/flyweight/extension2 -gocode/src/flyweight/otf/*.sbeir +gocode/*/pkg +gocode/struct/src/*/*.go +!gocode/struct/src/*/*_test.go +gocode/struct/src/*/*/*.go +!gocode/struct/src/*/*/*_test.go +gocode/struct/src/example-schema/example-schema* +gocode/struct/src/example-socket-clientserver/example-socket-clientserver +gocode/flyweight/src/*/*.go +!gocode/flyweight/src/*/*_test.go +gocode/flyweight/src/*/*/*.go +!gocode/flyweight/src/*/*/*_test.go # csharp csharp/*/bin diff --git a/build.gradle b/build.gradle index 43a6d93537..5cafcee377 100644 --- a/build.gradle +++ b/build.gradle @@ -699,7 +699,7 @@ tasks.register('generateGolangCodecTestComposite', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/struct', + 'sbe.output.dir': 'gocode/struct/src', 'sbe.target.language': 'golang') args = ['sbe-tool/src/test/resources/composite-elements-schema-rc4.xml'] } @@ -708,7 +708,7 @@ tasks.register('generateGolangCodecTestBasic', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/struct/basic', + 'sbe.output.dir': 'gocode/struct/src/basic', 'sbe.target.language': 'golang') args = ['sbe-tool/src/test/resources/basic-types-schema.xml'] } @@ -717,7 +717,7 @@ tasks.register('generateGolangCodecTestGroup', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/struct/group', + 'sbe.output.dir': 'gocode/struct/src/group', 'sbe.target.language': 'golang') args = ['sbe-tool/src/test/resources/basic-group-schema.xml'] } @@ -726,7 +726,7 @@ tasks.register('generateGolangCodecTestVarData', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/struct/vardata', + 'sbe.output.dir': 'gocode/struct/src/vardata', 'sbe.target.language': 'golang') args = ['sbe-tool/src/test/resources/basic-variable-length-schema.xml'] } @@ -735,7 +735,7 @@ tasks.register('generateGolangCodecsWithXIncludes', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/struct', + 'sbe.output.dir': 'gocode/struct/src', 'sbe.target.language': 'golang', 'sbe.xinclude.aware': 'true', 'sbe.validation.xsd': validationXsdPath) @@ -747,7 +747,7 @@ tasks.register('generateGolangCodecsWithXSD', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/struct', + 'sbe.output.dir': 'gocode/struct/src', 'sbe.target.language': 'golang', 'sbe.xinclude.aware': 'true', 'sbe.validation.xsd': validationXsdPath) @@ -773,7 +773,7 @@ tasks.register('generateGolangFlyweightCodecTestComposite', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/flyweight', + 'sbe.output.dir': 'gocode/flyweight/src', 'sbe.go.generate.generate.flyweights': 'true', 'sbe.target.language': 'golang') args = ['sbe-tool/src/test/resources/composite-elements-schema-rc4.xml'] @@ -783,7 +783,7 @@ tasks.register('generateGolangFlyweightCodecTestBasic', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/flyweight/basic', + 'sbe.output.dir': 'gocode/flyweight/src/basic', 'sbe.go.generate.generate.flyweights': 'true', 'sbe.target.language': 'golang') args = ['sbe-tool/src/test/resources/basic-types-schema.xml'] @@ -793,7 +793,7 @@ tasks.register('generateGolangFlyweightCodecTestGroup', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/flyweight/group', + 'sbe.output.dir': 'gocode/flyweight/src/group', 'sbe.go.generate.generate.flyweights': 'true', 'sbe.target.language': 'golang') args = ['sbe-tool/src/test/resources/basic-group-schema.xml'] @@ -803,7 +803,7 @@ tasks.register('generateGolangFlyweightCodecTestVarData', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/flyweight/vardata', + 'sbe.output.dir': 'gocode/flyweight/src/vardata', 'sbe.go.generate.generate.flyweights': 'true', 'sbe.target.language': 'golang') args = ['sbe-tool/src/test/resources/basic-variable-length-schema.xml'] @@ -813,7 +813,7 @@ tasks.register('generateGolangFlyweightCodecsWithXIncludes', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/flyweight', + 'sbe.output.dir': 'gocode/flyweight/src', 'sbe.go.generate.generate.flyweights': 'true', 'sbe.target.language': 'golang', 'sbe.xinclude.aware': 'true', @@ -826,7 +826,7 @@ tasks.register('generateGolangFlyweightCodecsWithXSD', JavaExec) { mainClass.set('uk.co.real_logic.sbe.SbeTool') classpath = project(':sbe-tool').sourceSets.main.runtimeClasspath systemProperties( - 'sbe.output.dir': 'gocode/src/flyweight', + 'sbe.output.dir': 'gocode/flyweight/src', 'sbe.go.generate.generate.flyweights': 'true', 'sbe.target.language': 'golang', 'sbe.xinclude.aware': 'true', diff --git a/gocode/Makefile b/gocode/Makefile index 0acd432c73..005bb1ddb4 100644 --- a/gocode/Makefile +++ b/gocode/Makefile @@ -19,11 +19,11 @@ all: example test # bench # This target is used to build golang files using parent gradle # script's invocation of sbe-tool in case it needs to be run again. We # use this one output file to check that dependency as it's simple -DEP=src/struct/simple/SbeMarshalling.go +DEP=struct/src/simple/SbeMarshalling.go $(DEP): $(SBE_JAR) (cd ..; ./gradlew generateGolangCodecs) - (export GOPATH=$(GOPATH) && \ - cd src/struct/simple && \ + (export GOPATH=$(GOPATH)/struct && \ + cd struct/src/simple && \ go build && \ $(SAVE_FORMAT) \ go fmt && \ @@ -31,27 +31,27 @@ $(DEP): $(SBE_JAR) # Will regenerate codecs by removing dependencies clean: - rm -f src/struct/*/SbeMarshalling.go src/struct/*/*/SbeMarshalling.go + rm -f struct/src/*/SbeMarshalling.go struct/src/*/*/SbeMarshalling.go -# Example code and benchmarking -example: src/struct/example-schema/example-schema -src/struct/example-schema/example-schema: $(DEP) - (export GOPATH=$(GOPATH) && \ - cd src/struct/example-schema && \ +# Example code and benchmarking +example: struct/src/example-schema/example-schema +struct/src/example-schema/example-schema: $(DEP) + (export GOPATH=$(GOPATH)/struct && \ + cd struct/src/example-schema && \ go build && \ go fmt && \ ./example-schema) bench: $(DEP) - (export GOPATH=$(GOPATH) && \ - cd src/struct/example-schema && \ + (export GOPATH=$(GOPATH)/struct && \ + cd struct/src/example-schema && \ go test --bench . -cpuprofile=cpu.out && \ go tool pprof -text example-schema.test cpu.out) -src/struct/baseline/SbeMarshalling.go: $(DEP) +struct/src/baseline/SbeMarshalling.go: $(DEP) $(GOPATH)/sbe-tool -d src -s $(EXAMPLES_SCHEMA) - (export GOPATH=$(GOPATH) && \ - cd src/struct/baseline && \ + (export GOPATH=$(GOPATH)/struct && \ + cd struct/src/baseline && \ go build && \ go fmt && \ go test && \ @@ -59,51 +59,51 @@ src/struct/baseline/SbeMarshalling.go: $(DEP) # The first set does a make install as there is a test that uses # multiple packages and needs them in GOPATH The second set work in -# src/struct/foo, and the third need a GOPATH addition as for Java and C++ +# struct/src/foo, and the third need a GOPATH addition as for Java and C++ # they generate into the same directory but golang doesn't allow that test: $(DEP) - (export GOPATH=$(GOPATH) && \ + (export GOPATH=$(GOPATH)/struct && \ (for t in baseline extension; do \ - export GOPATH=$(GOPATH) && \ - cd $(GOPATH)/src/struct/$$t && \ + export GOPATH=$(GOPATH)/struct && \ + cd $(GOPATH)/struct/src/$$t && \ go build && \ go fmt && \ go test && \ go install \ ;done)) - (export GOPATH=$(GOPATH) && \ + (export GOPATH=$(GOPATH)/struct && \ (for t in baseline-bigendian mktdata group_with_data group_with_data_extension composite_elements composite since-deprecated simple issue435 issue472 issue483 issue488 issue560 issue847 issue848 issue849 issue505 test973; do \ - cd $(GOPATH)/src/struct/$$t && \ + cd $(GOPATH)/struct/src/$$t && \ go build && \ go fmt && \ go test \ ;done)) (for t in vardata group basic; do \ - export GOPATH=$(GOPATH)/$$t && \ - cd $(GOPATH)/src/struct/$$t/'SBE tests' && \ + export GOPATH=$(GOPATH)/struct/$$t && \ + cd $(GOPATH)/struct/src/$$t/'SBE tests' && \ go build && \ go fmt && \ go test \ ;done) - (export GOPATH=$(GOPATH) && \ + (export GOPATH=$(GOPATH)/flyweight && \ (for t in baseline extension; do \ - export GOPATH=$(GOPATH) && \ - cd $(GOPATH)/src/flyweight/$$t && \ + export GOPATH=$(GOPATH)/flyweight && \ + cd $(GOPATH)/flyweight/src/$$t && \ go build && \ go fmt && \ go test && \ go install \ ;done)) - (export GOPATH=$(GOPATH) && \ + (export GOPATH=$(GOPATH)/flyweight && \ (for t in baseline-bigendian mktdata group_with_data group_with_data_extension composite_elements composite since-deprecated simple issue435 issue472 issue483 issue488 issue560 issue847 issue848 issue849; do \ - cd $(GOPATH)/src/flyweight/$$t && \ + cd $(GOPATH)/flyweight/src/$$t && \ go build && \ go fmt && \ go test \ ;done)) (for t in vardata group basic; do \ - export GOPATH=$(GOPATH)/$$t && \ - cd $(GOPATH)/src/flyweight/$$t/'SBE tests' && \ + export GOPATH=$(GOPATH)/flyweight/$$t && \ + cd $(GOPATH)/flyweight/src/$$t/'SBE tests' && \ go build && \ go fmt && \ go test \ diff --git a/gocode/src/flyweight/baseline-bigendian/Car_test.go b/gocode/flyweight/src/baseline-bigendian/Car_test.go similarity index 100% rename from gocode/src/flyweight/baseline-bigendian/Car_test.go rename to gocode/flyweight/src/baseline-bigendian/Car_test.go diff --git a/gocode/src/flyweight/baseline/Car_test.go b/gocode/flyweight/src/baseline/Car_test.go similarity index 100% rename from gocode/src/flyweight/baseline/Car_test.go rename to gocode/flyweight/src/baseline/Car_test.go diff --git a/gocode/src/flyweight/basic/SBE tests/ENUM_test.go b/gocode/flyweight/src/basic/SBE tests/ENUM_test.go similarity index 100% rename from gocode/src/flyweight/basic/SBE tests/ENUM_test.go rename to gocode/flyweight/src/basic/SBE tests/ENUM_test.go diff --git a/gocode/src/flyweight/basic/SBE tests/Message1_test.go b/gocode/flyweight/src/basic/SBE tests/Message1_test.go similarity index 100% rename from gocode/src/flyweight/basic/SBE tests/Message1_test.go rename to gocode/flyweight/src/basic/SBE tests/Message1_test.go diff --git a/gocode/src/flyweight/basic/SBE tests/SET_test.go b/gocode/flyweight/src/basic/SBE tests/SET_test.go similarity index 100% rename from gocode/src/flyweight/basic/SBE tests/SET_test.go rename to gocode/flyweight/src/basic/SBE tests/SET_test.go diff --git a/gocode/src/flyweight/composite/Composite_test.go b/gocode/flyweight/src/composite/Composite_test.go similarity index 100% rename from gocode/src/flyweight/composite/Composite_test.go rename to gocode/flyweight/src/composite/Composite_test.go diff --git a/gocode/src/flyweight/composite_elements/Message_test.go b/gocode/flyweight/src/composite_elements/Message_test.go similarity index 100% rename from gocode/src/flyweight/composite_elements/Message_test.go rename to gocode/flyweight/src/composite_elements/Message_test.go diff --git a/gocode/src/flyweight/example-schema/CarExample_test.go b/gocode/flyweight/src/example-schema/CarExample_test.go similarity index 100% rename from gocode/src/flyweight/example-schema/CarExample_test.go rename to gocode/flyweight/src/example-schema/CarExample_test.go diff --git a/gocode/src/flyweight/group/SBE tests/TestMessage1_test.go b/gocode/flyweight/src/group/SBE tests/TestMessage1_test.go similarity index 100% rename from gocode/src/flyweight/group/SBE tests/TestMessage1_test.go rename to gocode/flyweight/src/group/SBE tests/TestMessage1_test.go diff --git a/gocode/src/flyweight/group_with_data/TestMessages_test.go b/gocode/flyweight/src/group_with_data/TestMessages_test.go similarity index 100% rename from gocode/src/flyweight/group_with_data/TestMessages_test.go rename to gocode/flyweight/src/group_with_data/TestMessages_test.go diff --git a/gocode/src/flyweight/group_with_data_extension/Extension_test.go b/gocode/flyweight/src/group_with_data_extension/Extension_test.go similarity index 99% rename from gocode/src/flyweight/group_with_data_extension/Extension_test.go rename to gocode/flyweight/src/group_with_data_extension/Extension_test.go index afce620e0c..2f9547b94a 100644 --- a/gocode/src/flyweight/group_with_data_extension/Extension_test.go +++ b/gocode/flyweight/src/group_with_data_extension/Extension_test.go @@ -1,8 +1,8 @@ package group_with_data_extension import ( - "flyweight/group_with_data" "fmt" + "group_with_data" "math" "testing" ) diff --git a/gocode/src/flyweight/issue435/Issue435_test.go b/gocode/flyweight/src/issue435/Issue435_test.go similarity index 100% rename from gocode/src/flyweight/issue435/Issue435_test.go rename to gocode/flyweight/src/issue435/Issue435_test.go diff --git a/gocode/src/flyweight/issue472/Issue472_test.go b/gocode/flyweight/src/issue472/Issue472_test.go similarity index 100% rename from gocode/src/flyweight/issue472/Issue472_test.go rename to gocode/flyweight/src/issue472/Issue472_test.go diff --git a/gocode/src/flyweight/issue483/Issue483_test.go b/gocode/flyweight/src/issue483/Issue483_test.go similarity index 100% rename from gocode/src/flyweight/issue483/Issue483_test.go rename to gocode/flyweight/src/issue483/Issue483_test.go diff --git a/gocode/src/flyweight/issue488/Issue488_test.go b/gocode/flyweight/src/issue488/Issue488_test.go similarity index 100% rename from gocode/src/flyweight/issue488/Issue488_test.go rename to gocode/flyweight/src/issue488/Issue488_test.go diff --git a/gocode/src/flyweight/issue560/Issue560_test.go b/gocode/flyweight/src/issue560/Issue560_test.go similarity index 100% rename from gocode/src/flyweight/issue560/Issue560_test.go rename to gocode/flyweight/src/issue560/Issue560_test.go diff --git a/gocode/src/flyweight/issue847/issue847_test.go b/gocode/flyweight/src/issue847/issue847_test.go similarity index 100% rename from gocode/src/flyweight/issue847/issue847_test.go rename to gocode/flyweight/src/issue847/issue847_test.go diff --git a/gocode/src/flyweight/issue848/issue848_test.go b/gocode/flyweight/src/issue848/issue848_test.go similarity index 100% rename from gocode/src/flyweight/issue848/issue848_test.go rename to gocode/flyweight/src/issue848/issue848_test.go diff --git a/gocode/src/flyweight/issue849/issue849_test.go b/gocode/flyweight/src/issue849/issue849_test.go similarity index 100% rename from gocode/src/flyweight/issue849/issue849_test.go rename to gocode/flyweight/src/issue849/issue849_test.go diff --git a/gocode/src/flyweight/mktdata/fixme_test.go b/gocode/flyweight/src/mktdata/fixme_test.go similarity index 100% rename from gocode/src/flyweight/mktdata/fixme_test.go rename to gocode/flyweight/src/mktdata/fixme_test.go diff --git a/gocode/src/flyweight/otf/go.mod b/gocode/flyweight/src/otf/go.mod similarity index 100% rename from gocode/src/flyweight/otf/go.mod rename to gocode/flyweight/src/otf/go.mod diff --git a/gocode/src/flyweight/otf/go.sum b/gocode/flyweight/src/otf/go.sum similarity index 100% rename from gocode/src/flyweight/otf/go.sum rename to gocode/flyweight/src/otf/go.sum diff --git a/gocode/src/flyweight/otf/json_test.go b/gocode/flyweight/src/otf/json_test.go similarity index 100% rename from gocode/src/flyweight/otf/json_test.go rename to gocode/flyweight/src/otf/json_test.go diff --git a/gocode/src/flyweight/otf/otf_test.go b/gocode/flyweight/src/otf/otf_test.go similarity index 100% rename from gocode/src/flyweight/otf/otf_test.go rename to gocode/flyweight/src/otf/otf_test.go diff --git a/gocode/src/flyweight/otf/testlistener.go b/gocode/flyweight/src/otf/testlistener.go similarity index 100% rename from gocode/src/flyweight/otf/testlistener.go rename to gocode/flyweight/src/otf/testlistener.go diff --git a/gocode/src/flyweight/simple/simple_test.go b/gocode/flyweight/src/simple/simple_test.go similarity index 100% rename from gocode/src/flyweight/simple/simple_test.go rename to gocode/flyweight/src/simple/simple_test.go diff --git a/gocode/src/flyweight/since-deprecated/SinceDeprecated_test.go b/gocode/flyweight/src/since-deprecated/SinceDeprecated_test.go similarity index 100% rename from gocode/src/flyweight/since-deprecated/SinceDeprecated_test.go rename to gocode/flyweight/src/since-deprecated/SinceDeprecated_test.go diff --git a/gocode/src/flyweight/vardata/SBE tests/TestMessage1_test.go b/gocode/flyweight/src/vardata/SBE tests/TestMessage1_test.go similarity index 100% rename from gocode/src/flyweight/vardata/SBE tests/TestMessage1_test.go rename to gocode/flyweight/src/vardata/SBE tests/TestMessage1_test.go diff --git a/gocode/src/struct/baseline-bigendian/Car_test.go b/gocode/struct/src/baseline-bigendian/Car_test.go similarity index 100% rename from gocode/src/struct/baseline-bigendian/Car_test.go rename to gocode/struct/src/baseline-bigendian/Car_test.go diff --git a/gocode/src/struct/baseline/Car_test.go b/gocode/struct/src/baseline/Car_test.go similarity index 100% rename from gocode/src/struct/baseline/Car_test.go rename to gocode/struct/src/baseline/Car_test.go diff --git a/gocode/src/struct/basic/SBE tests/ENUM_test.go b/gocode/struct/src/basic/SBE tests/ENUM_test.go similarity index 100% rename from gocode/src/struct/basic/SBE tests/ENUM_test.go rename to gocode/struct/src/basic/SBE tests/ENUM_test.go diff --git a/gocode/src/struct/basic/SBE tests/Message1_test.go b/gocode/struct/src/basic/SBE tests/Message1_test.go similarity index 100% rename from gocode/src/struct/basic/SBE tests/Message1_test.go rename to gocode/struct/src/basic/SBE tests/Message1_test.go diff --git a/gocode/src/struct/basic/SBE tests/SET_test.go b/gocode/struct/src/basic/SBE tests/SET_test.go similarity index 100% rename from gocode/src/struct/basic/SBE tests/SET_test.go rename to gocode/struct/src/basic/SBE tests/SET_test.go diff --git a/gocode/src/struct/composite/Composite_test.go b/gocode/struct/src/composite/Composite_test.go similarity index 100% rename from gocode/src/struct/composite/Composite_test.go rename to gocode/struct/src/composite/Composite_test.go diff --git a/gocode/src/struct/composite_elements/Message_test.go b/gocode/struct/src/composite_elements/Message_test.go similarity index 100% rename from gocode/src/struct/composite_elements/Message_test.go rename to gocode/struct/src/composite_elements/Message_test.go diff --git a/gocode/src/struct/example-schema/CarExample.go b/gocode/struct/src/example-schema/CarExample.go similarity index 100% rename from gocode/src/struct/example-schema/CarExample.go rename to gocode/struct/src/example-schema/CarExample.go diff --git a/gocode/src/struct/example-schema/CarExample_test.go b/gocode/struct/src/example-schema/CarExample_test.go similarity index 100% rename from gocode/src/struct/example-schema/CarExample_test.go rename to gocode/struct/src/example-schema/CarExample_test.go diff --git a/gocode/src/struct/example-socket-clientserver/clientserver.go b/gocode/struct/src/example-socket-clientserver/clientserver.go similarity index 100% rename from gocode/src/struct/example-socket-clientserver/clientserver.go rename to gocode/struct/src/example-socket-clientserver/clientserver.go diff --git a/gocode/src/struct/group/SBE tests/TestMessage1_test.go b/gocode/struct/src/group/SBE tests/TestMessage1_test.go similarity index 100% rename from gocode/src/struct/group/SBE tests/TestMessage1_test.go rename to gocode/struct/src/group/SBE tests/TestMessage1_test.go diff --git a/gocode/src/struct/group_with_data/TestMessages_test.go b/gocode/struct/src/group_with_data/TestMessages_test.go similarity index 100% rename from gocode/src/struct/group_with_data/TestMessages_test.go rename to gocode/struct/src/group_with_data/TestMessages_test.go diff --git a/gocode/src/struct/group_with_data_extension/Extension_test.go b/gocode/struct/src/group_with_data_extension/Extension_test.go similarity index 99% rename from gocode/src/struct/group_with_data_extension/Extension_test.go rename to gocode/struct/src/group_with_data_extension/Extension_test.go index 0c79daafb8..cb245d64d8 100644 --- a/gocode/src/struct/group_with_data_extension/Extension_test.go +++ b/gocode/struct/src/group_with_data_extension/Extension_test.go @@ -3,8 +3,8 @@ package group_with_data_extension import ( "bytes" "fmt" + "group_with_data" "math" - "struct/group_with_data" "testing" ) diff --git a/gocode/src/struct/issue435/Issue435_test.go b/gocode/struct/src/issue435/Issue435_test.go similarity index 100% rename from gocode/src/struct/issue435/Issue435_test.go rename to gocode/struct/src/issue435/Issue435_test.go diff --git a/gocode/src/struct/issue472/Issue472_test.go b/gocode/struct/src/issue472/Issue472_test.go similarity index 100% rename from gocode/src/struct/issue472/Issue472_test.go rename to gocode/struct/src/issue472/Issue472_test.go diff --git a/gocode/src/struct/issue483/Issue483_test.go b/gocode/struct/src/issue483/Issue483_test.go similarity index 100% rename from gocode/src/struct/issue483/Issue483_test.go rename to gocode/struct/src/issue483/Issue483_test.go diff --git a/gocode/src/struct/issue488/Issue488_test.go b/gocode/struct/src/issue488/Issue488_test.go similarity index 100% rename from gocode/src/struct/issue488/Issue488_test.go rename to gocode/struct/src/issue488/Issue488_test.go diff --git a/gocode/src/issue505/Issue505_test.go b/gocode/struct/src/issue505/Issue505_test.go similarity index 100% rename from gocode/src/issue505/Issue505_test.go rename to gocode/struct/src/issue505/Issue505_test.go diff --git a/gocode/src/struct/issue560/Issue560_test.go b/gocode/struct/src/issue560/Issue560_test.go similarity index 100% rename from gocode/src/struct/issue560/Issue560_test.go rename to gocode/struct/src/issue560/Issue560_test.go diff --git a/gocode/src/struct/issue847/issue847_test.go b/gocode/struct/src/issue847/issue847_test.go similarity index 100% rename from gocode/src/struct/issue847/issue847_test.go rename to gocode/struct/src/issue847/issue847_test.go diff --git a/gocode/src/struct/issue848/issue848_test.go b/gocode/struct/src/issue848/issue848_test.go similarity index 100% rename from gocode/src/struct/issue848/issue848_test.go rename to gocode/struct/src/issue848/issue848_test.go diff --git a/gocode/src/struct/issue849/issue849_test.go b/gocode/struct/src/issue849/issue849_test.go similarity index 100% rename from gocode/src/struct/issue849/issue849_test.go rename to gocode/struct/src/issue849/issue849_test.go diff --git a/gocode/src/struct/mktdata/fixme_test.go b/gocode/struct/src/mktdata/fixme_test.go similarity index 100% rename from gocode/src/struct/mktdata/fixme_test.go rename to gocode/struct/src/mktdata/fixme_test.go diff --git a/gocode/src/struct/mktdata/preallocated_test.go b/gocode/struct/src/mktdata/preallocated_test.go similarity index 100% rename from gocode/src/struct/mktdata/preallocated_test.go rename to gocode/struct/src/mktdata/preallocated_test.go diff --git a/gocode/src/struct/simple/simple_test.go b/gocode/struct/src/simple/simple_test.go similarity index 100% rename from gocode/src/struct/simple/simple_test.go rename to gocode/struct/src/simple/simple_test.go diff --git a/gocode/src/struct/since-deprecated/SinceDeprecated_test.go b/gocode/struct/src/since-deprecated/SinceDeprecated_test.go similarity index 100% rename from gocode/src/struct/since-deprecated/SinceDeprecated_test.go rename to gocode/struct/src/since-deprecated/SinceDeprecated_test.go diff --git a/gocode/src/test973/EventType.go b/gocode/struct/src/test973/EventType.go similarity index 100% rename from gocode/src/test973/EventType.go rename to gocode/struct/src/test973/EventType.go diff --git a/gocode/src/test973/MessageHeader.go b/gocode/struct/src/test973/MessageHeader.go similarity index 100% rename from gocode/src/test973/MessageHeader.go rename to gocode/struct/src/test973/MessageHeader.go diff --git a/gocode/src/test973/SbeMarshalling.go b/gocode/struct/src/test973/SbeMarshalling.go similarity index 100% rename from gocode/src/test973/SbeMarshalling.go rename to gocode/struct/src/test973/SbeMarshalling.go diff --git a/gocode/src/test973/SomeMessage.go b/gocode/struct/src/test973/SomeMessage.go similarity index 100% rename from gocode/src/test973/SomeMessage.go rename to gocode/struct/src/test973/SomeMessage.go diff --git a/gocode/src/test973/test973_test.go b/gocode/struct/src/test973/test973_test.go similarity index 100% rename from gocode/src/test973/test973_test.go rename to gocode/struct/src/test973/test973_test.go diff --git a/gocode/src/struct/vardata/SBE tests/TestMessage1_test.go b/gocode/struct/src/vardata/SBE tests/TestMessage1_test.go similarity index 100% rename from gocode/src/struct/vardata/SBE tests/TestMessage1_test.go rename to gocode/struct/src/vardata/SBE tests/TestMessage1_test.go From 69c45a3c0b563fac37b187d40dd7b545b882800d Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Tue, 17 Sep 2024 20:32:38 -0400 Subject: [PATCH 19/21] [Go] Fix copywrite year --- sbe-tool/src/main/golang/otf/encoding.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbe-tool/src/main/golang/otf/encoding.go b/sbe-tool/src/main/golang/otf/encoding.go index d08ea86a61..64cf6a4dbf 100644 --- a/sbe-tool/src/main/golang/otf/encoding.go +++ b/sbe-tool/src/main/golang/otf/encoding.go @@ -1,4 +1,4 @@ -// Copyright 2013-2023 Real Logic Limited. +// Copyright 2013-2024 Real Logic Limited. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From 46918b8be2b4299520a7b7d0ae3ad161b79953fe Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Tue, 17 Sep 2024 21:45:19 -0400 Subject: [PATCH 20/21] [Go] Fix tests for otf This generates flyweights in the sbe-tool build for use in the otf library. --- .gitignore | 2 ++ build.gradle | 2 ++ gocode/Makefile | 7 +++++++ gocode/flyweight/src/otf/otf_test.go | 2 +- 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 363f445f20..4e46271843 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,7 @@ cppbuild/Release cppbuild/Win32 # golang +gocode/pkg gocode/*/pkg gocode/struct/src/*/*.go !gocode/struct/src/*/*_test.go @@ -75,6 +76,7 @@ gocode/flyweight/src/*/*.go !gocode/flyweight/src/*/*_test.go gocode/flyweight/src/*/*/*.go !gocode/flyweight/src/*/*/*_test.go +gocode/flyweight/src/otf/code-generation-schema.sbeir # csharp csharp/*/bin diff --git a/build.gradle b/build.gradle index 5cafcee377..8122f780c6 100644 --- a/build.gradle +++ b/build.gradle @@ -865,6 +865,7 @@ tasks.register('generateGolangCodecs') { 'generateGolangFlyweightCodecTestComposite', 'generateGolangFlyweightCodecsWithXIncludes', 'generateGolangFlyweightCodecsWithXSD', + 'generateIrCodecs', ':sbe-all:jar' } @@ -953,6 +954,7 @@ tasks.register('generateGolangIrCodecs', JavaExec) { systemProperties( 'sbe.output.dir': 'sbe-tool/src/main/golang', 'sbe.target.language': 'golang', + 'sbe.go.generate.generate.flyweights': true, 'sbe.validation.xsd': validationXsdPath) args = ['sbe-tool/src/main/resources/sbe-ir.xml'] } diff --git a/gocode/Makefile b/gocode/Makefile index 005bb1ddb4..a635c61f1d 100644 --- a/gocode/Makefile +++ b/gocode/Makefile @@ -108,3 +108,10 @@ test: $(DEP) go fmt && \ go test \ ;done) + (export SBE_JAR=$(SBE_JAR) && \ + export GO111MODULE=on && \ + (cd $(GOPATH)/flyweight/src/otf && \ + go generate && \ + go build && \ + go fmt && \ + go test)) diff --git a/gocode/flyweight/src/otf/otf_test.go b/gocode/flyweight/src/otf/otf_test.go index 87d0e3ed71..babb370693 100644 --- a/gocode/flyweight/src/otf/otf_test.go +++ b/gocode/flyweight/src/otf/otf_test.go @@ -7,7 +7,7 @@ import ( "testing" ) -//go:generate java -Dsbe.output.dir=. -Dsbe.target.language=golang -Dsbe.go.generate.generate.flyweights=true -Dsbe.generate.ir=true -jar ../../../../sbe-all/build/libs/sbe-all-1.30.0-SNAPSHOT.jar ../../../../sbe-tool/src/test/resources/code-generation-schema.xml +//go:generate java -Dsbe.output.dir=. -Dsbe.target.language=golang -Dsbe.go.generate.generate.flyweights=true -Dsbe.generate.ir=true -jar ../../../${SBE_JAR} ../../../../sbe-tool/src/test/resources/code-generation-schema.xml const SchemaFilename = "code-generation-schema.sbeir" const MsgBufferCapacity = 4 * 1024 From 9d3320717bb0a524dec38d612b29ba654e129b0c Mon Sep 17 00:00:00 2001 From: Ethan Feldman Date: Tue, 17 Sep 2024 22:25:55 -0400 Subject: [PATCH 21/21] [Go] Add helpers for otf PrimitiveValue --- sbe-tool/src/main/golang/go.sum | 17 ++ .../json/{jsonprinter.go => json_printer.go} | 0 ...okenlistener.go => json_token_listener.go} | 0 sbe-tool/src/main/golang/otf/encoding.go | 164 ++++++++++++++++++ 4 files changed, 181 insertions(+) create mode 100644 sbe-tool/src/main/golang/go.sum rename sbe-tool/src/main/golang/json/{jsonprinter.go => json_printer.go} (100%) rename sbe-tool/src/main/golang/json/{jsontokenlistener.go => json_token_listener.go} (100%) diff --git a/sbe-tool/src/main/golang/go.sum b/sbe-tool/src/main/golang/go.sum new file mode 100644 index 0000000000..479781e603 --- /dev/null +++ b/sbe-tool/src/main/golang/go.sum @@ -0,0 +1,17 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/sbe-tool/src/main/golang/json/jsonprinter.go b/sbe-tool/src/main/golang/json/json_printer.go similarity index 100% rename from sbe-tool/src/main/golang/json/jsonprinter.go rename to sbe-tool/src/main/golang/json/json_printer.go diff --git a/sbe-tool/src/main/golang/json/jsontokenlistener.go b/sbe-tool/src/main/golang/json/json_token_listener.go similarity index 100% rename from sbe-tool/src/main/golang/json/jsontokenlistener.go rename to sbe-tool/src/main/golang/json/json_token_listener.go diff --git a/sbe-tool/src/main/golang/otf/encoding.go b/sbe-tool/src/main/golang/otf/encoding.go index 64cf6a4dbf..a10fef5b2d 100644 --- a/sbe-tool/src/main/golang/otf/encoding.go +++ b/sbe-tool/src/main/golang/otf/encoding.go @@ -260,6 +260,158 @@ func NewPrimitiveValue(t PrimitiveType, value []byte) PrimitiveValue { return p } +func ParsePrimitiveValue(t PrimitiveType, value []byte) (PrimitiveValue, error) { + p := PrimitiveValue{PrimitiveType: t} + + var err error + + switch t { + case CHAR: + if len(value) > 1 { + p.arrayValue = string(value) + p.size = len(value) + } else { + p.asInt = int64(value[0]) + p.size = 1 + } + case INT8: + p.asInt, err = strconv.ParseInt(string(value), 10, 8) + p.size = 1 + case INT16: + p.asInt, err = strconv.ParseInt(string(value), 10, 16) + p.size = 2 + case INT32: + p.asInt, err = strconv.ParseInt(string(value), 10, 32) + p.size = 4 + case INT64: + p.asInt, err = strconv.ParseInt(string(value), 10, 64) + p.size = 8 + case UINT8: + p.asUInt, err = strconv.ParseUint(string(value), 10, 8) + p.size = 1 + case UINT16: + p.asUInt, err = strconv.ParseUint(string(value), 10, 16) + p.size = 2 + case UINT32: + p.asUInt, err = strconv.ParseUint(string(value), 10, 32) + p.size = 4 + case UINT64: + p.asUInt, err = strconv.ParseUint(string(value), 10, 64) + p.size = 8 + case FLOAT: + p.asDouble, err = strconv.ParseFloat(string(value), 32) + p.size = 4 + case DOUBLE: + p.asDouble, err = strconv.ParseFloat(string(value), 64) + p.size = 8 + default: + p.PrimitiveType = NONE + p.size = 0 + } + + return p, err +} + +func NewNoneValue() PrimitiveValue { + return PrimitiveValue{} +} + +func NewCharValue(value byte) PrimitiveValue { + return PrimitiveValue{ + PrimitiveType: CHAR, + size: 1, + asInt: int64(value), + } +} + +func NewStringValue(value string) PrimitiveValue { + return PrimitiveValue{ + PrimitiveType: CHAR, + size: len(value), + arrayValue: value, + } +} + +func NewInt8Value(value int8) PrimitiveValue { + return PrimitiveValue{ + PrimitiveType: INT8, + size: 1, + asInt: int64(value), + } +} + +func NewInt16Value(value int16) PrimitiveValue { + return PrimitiveValue{ + PrimitiveType: INT16, + size: 2, + asInt: int64(value), + } +} + +func NewInt32Value(value int32) PrimitiveValue { + return PrimitiveValue{ + PrimitiveType: INT32, + size: 4, + asInt: int64(value), + } +} + +func NewInt64Value(value int64) PrimitiveValue { + return PrimitiveValue{ + PrimitiveType: INT64, + size: 8, + asInt: value, + } +} + +func NewUInt8Value(value uint8) PrimitiveValue { + return PrimitiveValue{ + PrimitiveType: UINT8, + size: 1, + asUInt: uint64(value), + } +} + +func NewUInt16Value(value uint16) PrimitiveValue { + return PrimitiveValue{ + PrimitiveType: UINT16, + size: 2, + asUInt: uint64(value), + } +} + +func NewUInt32Value(value uint32) PrimitiveValue { + return PrimitiveValue{ + PrimitiveType: UINT32, + size: 4, + asUInt: uint64(value), + } +} + +func NewUInt64Value(value uint64) PrimitiveValue { + return PrimitiveValue{ + PrimitiveType: UINT64, + size: 8, + asUInt: value, + } +} + +func NewFloatValue(value float32) PrimitiveValue { + return PrimitiveValue{ + PrimitiveType: FLOAT, + size: 4, + asDouble: float64(value), + } +} + +func NewDoubleValue(value float64) PrimitiveValue { + return PrimitiveValue{ + PrimitiveType: DOUBLE, + size: 8, + asDouble: value, + } +} + func (p *PrimitiveValue) AsInt() int64 { return p.asInt } @@ -276,6 +428,18 @@ func (p *PrimitiveValue) GetArray() string { return p.arrayValue } +func (p *PrimitiveValue) AsString() string { + if p.size <= 1 { + return string(byte(p.asInt)) + } + for i := 0; i < len(p.arrayValue); i++ { + if p.arrayValue[i] == 0 { + return p.arrayValue[:i] + } + } + return p.arrayValue +} + func (p *PrimitiveValue) Size() int { return p.size }